Frage

I have a rather large application I'm trying to make in Visual-C++, and I am now trying to add undo/redo functionality.

Having a rather large amount of events (button clicks, label text changed, etc.), I would like to find a way to undo/redo without adding code to every function. For Instance, I would like a class that can read every event done and store it automatically. Then in my undo/redo events, I can just get the latest action stored.

If that is not possible, I would not mind some other way.

Any help?

War es hilfreich?

Lösung

Declare a class that represent two operations - undo and redo. Also create two vectors of that class.

For each operation you want to apply undo/redo, push an instance of that class into the undo vector. There should be as many derived classes as there are opreations you want to undo.

For example, if a button click paints the background to green, you create a class instance whose undo metdho paints the background to the previous color, and its redo method paints the background to green, and stuff it into the undo vector. When you undo - you pop the last class instance and call its undo method, which will paint the background to the previous color. Then you push it to the redo vector.

When you redo, you pop the redo vector for the class instance at the top and invoke its redo method, them stuff it back to the undo vector.

There are some corner cases (boundaries), you'll tackle them when encountered.. :-)

Andere Tipps

Do all of your events pass through a queue of some kind? by default in c++ there is no queue like this (there is a windows os level event queue, but that is likely managed already and unusable in c++-cli and you did not indicate if this closely mapped onto your problem), there may be some other construct I am unaware of.

If you have some central queue, then it is just a matter of capturing events as they pass through and knowing how to undo each action. If no central queue is present then I see no other way easier than changing each undo-able function to create an undo object of some kind and storing it in and undo queue of some kind.

In a pure .net or a C++ environment without a large central work queue I would make a class that is and undo entry, that implements a method/member function to undo and another to redo/do the work. But for just undo functionality, this could be just a .net delegate or a c style function pointer, and a list of arguments. If you make an action undo/redo class it could be a template or generic that stores pointers/delegates to the the do and undo functions, and a list of arguments from when it was originally called.

These can be run to undo the actions that have been done. They will be inserted into a queue container of some kind, the kind of container doesn't seem to matter as longer as it preserves order, your should pick the best std, .net or other container for your application. You can discard older ones when you no longer need them. When executed the entry last inserted into the queue must be removed to preserve consistency.

If you also need redo functionality, then your queue of actions done must be iterable, and it would be easiest to use the class that was and action had a method/member function that could undo/redo the desired actions. You would have and iterator, pointer, index or marker of some kind indicating how far back you have undone. Every time an undo is requested you must move the marker backward (earlier chronologically) and execute the undo command at that point in the queue. If a redo is requested then the current item indicated executes its redo instruction and then the iterator is advanced forward (chronologically) through the queue, or ignored (I presume) if you are at the forward-most item in the queue.

If you wanted to go off the deep end, which you have no way indicated you want to, you could center you app around the action queue. You might not need to change you functions implementing this approach. Your user interface (I assumed, could just as easily be your API) functions, insert actions (which support doing and undoing) into a queue, and then command the queue to do. You would not have to change your existing functions if their side effects are known and reversible. However, you would need to change all the callers to make actions instead of directly calling, and you would need to write counterparts that do the undoing.

I've tried to achieve something like that in a small experimental library: https://github.com/d-led/undoredo-cpp. It contains an implementation of a TransactionStore similar to what CodeChords man suggested. You might need to really add functionality to each of your undoable objects, and also take care of object lifetimes, in case your actions involve object construction or destruction

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top