Re: Best way to hook into the run loop?
Re: Best way to hook into the run loop?
- Subject: Re: Best way to hook into the run loop?
- From: Graham Cox <email@hidden>
- Date: Sat, 5 Dec 2009 11:36:37 +1100
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 (email@hidden)
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:
This email sent to email@hidden