Re: Action based undo or snapshot based undo?
Re: Action based undo or snapshot based undo?
- Subject: Re: Action based undo or snapshot based undo?
- From: Greg Titus <email@hidden>
- Date: Wed, 29 Jan 2003 09:37:20 -0800
On Tuesday, January 28, 2003, at 11:32 PM, Alexander Lamb wrote:
Hello,
As I understood, the NSUndoManager is based on the idea of recording a
series of actions needed if the user wants to undo what he just did.
In Bill Cheeseman's book, he recommands recording those actions in the
action methods rather than at the object level, because it reflects
more closely what the user is doing and allows to customize the undo
for an action that may involve several changes on objects attributes.
I totally disagree with Bill here (sorry Bill), and I think that that
is why you are running into problems.
You should register the changes at the object level, but you should set
the action name in the action methods.
Now, I am having trouble with this whole concept for a few reasons:
Firstly, registering undo everywhere a user may hit an action can
become rather complex because of the number of places where this might
happen. For example: the data source of a NSTableView. How can you
implement the undo in the data source method setting the new cell
value? Not very elegant!
Nope, sure isn't. But it's an easy place to know what the action name
should be, so that's all you should do in the data source method.
Another issue is calculations. Not everything can be undone by
actions! Imagine I have an object graph on which I need to perform a
calculation. Lots of attributes get changed and then I need to undo. I
can't register the contrary of an optimisation calculation (imagine I
am optimising a schedule for example). The only way here would be to
duplicate the whole object graph and upon an undo discard the new
object graph.
What you should be doing here is registering an undo invocation for
every individual attribute change. All of those attribute changes end
up in the same undo group (which is the whole reason why undo groups
exist). At the action level you just set the name to "Optimisation
Calculation" or whatever.
Voila, when you look at the Edit menu it shows "Undo Optimisation
Calculation", and when you hit cmd-z, all of those attribute changes
are set back to their original values. (And of course you _are_ using
notifications or some other method to redraw your view when the
attributes change rather than making your control or view layer redraw
itself, right? Otherwise, not only is undo/redo going to be more
difficult to get right, but you won't be able to implement AppleScript
support easily either.)
Now there may well be rare times when you are changing so much of your
model that storing a whole snapshot of the object graph is smaller and
faster than generating the big series of attribute changes. But this is
an optimization that you shouldn't worry about unless you profile your
app and find that it is an issue.
So, (and you will see my roots in EOF here:-), why not implement a
snapshot based undo. Like the EOControl layer of EOF? No need for the
same complexity since you really need only one editing context and no
SQL generation. Just the registration of attributes changes. Now, the
nice thing about that is that you could register in one snapshot all
the attributes that have changed in one event loop. Therefore an undo
is just a question of going back one set of attributes. And... it
works also when objects are changed "in the background" from a
calculation or a network access, and... last but not least: it could
allow notification of change in a standard way and allow automatic
update of UI elements (like the EOInterface layer in EOF).
You should be doing all this anyway with the existing implementation of
NSUndoManager. :-)
The only difference is that instead of storing a snapshot like
EOControl, NSUndoManager is more flexible because it can invoke
arbitrary method invocations instead of just calling set methods. But
usually you should end up using them exactly the same way if you
register your undo invocations in your object graph set attribute
methods.
Hope this helps,
- Greg
_______________________________________________
cocoa-dev mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/cocoa-dev
Do not post admin requests to the list. They will be ignored.