On 05/12/2009, at 5:02 AM, Quincey Morris wrote:

>> However, what about the bug where opening and closing a group but doing 
>> nothing in between creates a bogus empty undo task?  This comes into play 
>> when grouping things that occur over a series of events, for example mouse 
>> drags. It would be nice if I could just open a group on mouse down, and 
>> close it on mouse up. Depending on what the mouse actually does, it might 
>> not submit any undoable tasks to the undo manager. So this bug means that 
>> case has to be detected and the undo stack cleaned up. Ultimately this is 
>> the problem I'm trying to workaround - you would not believe how complicated 
>> it can get.
> 
> Weren't you talking about this in the context of writing your own undo 
> manager? In that case, detecting and discarding "empty" groups would be a 
> feature of your implementation.


Yes, my implementation doesn't have this bug.

But it's yet to be shown whether I can successfully replace NSUndoManager 
entirely (a whole new object, not a subclass) and have Cocoa accept it. There 
might be private parts of the framework using private NSUndoManager API that 
make this unworkable. Also, for all its flaws I'd prefer to work with a 
framework class than rewrite it, so I'm looking at both options.

> 
>> A related issue to the above is that if the mouse operation throws an 
>> exception (it generally shouldn't, but it can happen), the same group close 
>> and clean up has to be done. Otherwise Undo simply stops working because of 
>> the group imbalance.
> 
> If an exception is caught and handled, it's not clear why Mike's suggestion 
> should not still work perfectly. We don't have any reason, do we, to think 
> that a scheduled 'performSelector' is going to be interfered with by the 
> exception? (And you would, of course, *do* the 'performSelector' immediately 
> after opening the undo group, not later, so that it's always scheduled no 
> matter what happens later.)

Well, I've set up my implementation to work this way or as a runloop observer 
using a conditional compile, so I can experiment with both.

I have no reason to think that the exception should cause a problem either, and 
that's true in the case of NSUndoManager as well. However, my observation is 
that things do start acting 'weird' there when this happens, like the 
non-decrementing group level count. It's because I can't go into the black box 
of NSUndoManager that I have no idea why that's happening and what to do about 
it. Making my own undo manager means that at least I can have some hope of 
debugging it.

> Not sure if that's the only case you encounter where an undoable action spans 
> over more than one event. If it's just the mouse drag stuff why not 
> submitting the action when the mouse drag is complete?
> 
> From my point of view that would be the logical thing to do, adding undo 
> actions "along the trail" sounds wrong. What I expect to do in a drag is 
> changing a collection of objects from one state (before the drag) to another 
> (when the button is released). Everything in between is just interaction to 
> animate the drag but it doesn't change the state permanently, at least I 
> wouldn't expect it to.
> 
> As I said, I can't imagine a purpose for this. That doesn't mean that there 
> isn't something I hadn't thought of - in which case I'd like to be 
> enlightened.

Ideally you don't want undo tasks recorded "all along the trail". But consider 
that the data model might not know anything about what is changing a given 
property. Take the positioning of a shape in drawing by dragging it. The 
shape's 'location' property needs to be undoable, but this might be called once 
in response to setting it numerically through some UI, or repeatedly while 
dragging. The data model can't know when it should submit the undo task for the 
last time in a series because it can't tell when 'last' is. My solution to this 
is to implement task coalescing in the undo manager - if a series of tasks are 
submitted having identical targets and selectors within the same undo group, 
only the first is recorded and subsequent ones are dropped ('first' being easy 
to detect, 'last' being impossible). On the whole that works quite well, though 
there are a couple of cases where more than one property is changed repeatedly 
which prevents the coalescing from happening. That does result in a series of 
tasks which are 'replayed', which isn't perfect but still works.

If anyone can think of an elegant and straightforward alternative, please feel 
free to suggest it. I've found that the "obvious" solution of submitting an 
object's state at the start of a drag is actually really awkward and horrible 
in practice, since it requires that the controller needs to know in advance all 
of the relevant properties that will be changed, when in fact that's none of 
its business. The data model should be free to change anything it needs to in 
response to a higher-level command and submit its own undo tasks.

--Graham

_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to