Re: How to imitiate mouse move programmatically? [NSApp postEvent:atStart:] does not work...
Re: How to imitiate mouse move programmatically? [NSApp postEvent:atStart:] does not work...
- Subject: Re: How to imitiate mouse move programmatically? [NSApp postEvent:atStart:] does not work...
- From: Oleg Krupnov <email@hidden>
- Date: Tue, 3 Nov 2009 15:46:36 +0200
Graham, I really appreciate you taking your time to write such
detailed posts for me, but what I am trying to say is that I am doing
everything exactly as you suggest, following the best design patterns
(I think). There is a controller and it maintains the selection. The
view only draws the selection in its drawRect, and during drawRect it
queries the selection from the controller. The view can also cause the
selection to change in response to mouse input. That's when the view
performs hit testing of its items, in its mouseMoved handler. Then the
view asks the controller to change the current selection. Then the
controller calls back the view telling that selection has changed, and
the view calls setNeedsDisplay on itself.
The way the items are displayed, and therefore also how they are
hit-tested, is encapsulated inside the view. In other words, the
controller and the model don't have to know how the items appear and
how to hit test them in each kind of view. The view is responsible to
process the mouse events.
This all is pretty standard MVC pattern, I believe. No problem with that.
The need to refresh the view after animation is rather a tiny
exception from the otherwise solid implementation. And I am
considering the fake mouse event because it seems the most natural and
straightforward way of changing the state of the system in this case.
Think of it this way: while the animation is in progress, the user may
be moving the mouse around, but all those events were ignored, and
what matters is only the last position of the mouse at the moment when
the animation ends. So in a sense, the fake mouse move event is not so
much fake, but a single consolidated mouse event of all those events.
The way my system should respond to this event is exactly the same as
to an ordinary, non-fake mouse event.
If I don't generate that fake event and use some explicit methods, I
would have to do the following:
1. in controller, determine which view the mouse currently is over
(hit test the window)
2. localize the coordinate to that view and ask the view to hit test
its items for the coordinate
3. set the controller's selection to that item
This all, and exactly this, would happen all by itself, by the already
existing code, in case if I sent the fake mouse moved event.
On Tue, Nov 3, 2009 at 3:09 PM, Graham Cox <email@hidden> wrote:
>
> On 03/11/2009, at 11:15 PM, Oleg Krupnov wrote:
>
>> Maybe I'm not speaking clearly, but that's exactly what I'm trying to
>> do -- use a mouse event to cause a state change, but in this case the
>> mouse event would be fake. Mouse position is in no way part of the
>> view's state. I just want that at particular moment the view's state
>> becomes synchronized with the current mouse position, as if the mouse
>> did move.
>
> But why? State is an independent variable. It is coupled to the mouse
> location in your case, but it's independent nevertheless. If you like, state
> is the part of the view's output (because it causes the visual appearance to
> change) whereas events are always input. Don't conflate them - views are
> designed to keep the input (events) and outputs (drawing) handled in two
> completely independent phases.
>
>> There are multiple items in the view, and the hovered one of the items
>> should be highlighted. When the mouse event arrives, the view performs
>> hit testing of its items and determines which of the items is hovered.
>> If I make a setState method as you suggest, I would have to specify
>> which item to highlight. This would break the view's encapsulation,
>> because I would have to perform item hit testing externally.
>
> I don't really follow this. At some point any hit testing does have to be
> done externally, if you have multiple objects, either by you or by AppKit.
> Doing it yourself is easy, and I strongly recommend that approach as
> follows:
>
> 1. main view gets the localised mouse location.
> 2. it iterates over all of its sub-objects representing the items and asks
> each one to test itself against the mouse position which is passed to it.
> 3. The first item that returns yes is sent the appropriate -setState: to
> highlight it (and the main view also keeps track of this one as "the
> selected item").
> 4. the -setState: method invalidates the drawing area covered by the object.
> 5. drawing deals with the appearance change.
>
> This is the classic, no-nonsense, normal everyday approach. Tracking to get
> each individual object to handle its own mouse tracking independently is not
> only going to be hard to get right, it's likely to have poor performance as
> well. All you're really doing is shunting the mouse position detection and
> hit-testing off onto multiple NSTrackingAreas - it still has to be done and
> it's unlikely to be faster than a simple approach that can take advantage of
> knowledge of how your sub-objects work.
>
> (Side discussion: In fact, an improved design than the above where state is
> linked to the concept of selection is to maintain a separate list of the
> selected items. When the items are drawn, pass them the selected state on
> the fly by testing if they belong to the selection set or not. This avoids
> the maintenance of two state variables that need to be synchronised - one to
> mean 'this object is part of my selection' and another one within the object
> that indicates 'highlighted'. If you don't have to keep those two in synch
> they can never get out of synch, and management of the selection becomes
> independent of the objects that are selected themselves, which shouldn't
> really care, other than to draw differently accordingly).
>
>> Again, I could implement a custom "refresh" method in each view, which
>> would query the current mouse position and call the mouse moved event
>> handler, but in addition to the extra code, before calling this method
>> I would have to hit test the window to determine which view is
>> currently hovered, i.e. basically repeat what NSApp and NSWindow
>> normally do when dispatching mouse events. That's why I thought
>> simulating a mouse move event could be justified.
>
> This sounds backwards. Instead, just pass the mouse position down to the
> sub-object as needed, usually to perform a hit-test. (It's good for each
> object to implement its own hit-test method because you can customise it for
> different classes if necessary). The main view that contains all these other
> objects should act as a controller, responding to mouse events and asking
> the relevant objects to change state.
>
> If you find this repugnant for some reason (not sure why, it's what nearly
> all drawing apps actually do), another option is to make each sub-object a
> NSView subclass in its own right, so that the normal hit-testing and mouse
> dispatch is done for you. But this approach *is* discouraged by the docs for
> performance reasons - it's better to keep your sub-objects lightweight and
> let the main view do the work of grovelling over them. But even NSView has a
> -hitTest: method.
>
>> Also, beside this discussion of design, I'd appreciate to find out why
>> postEvent actually did not work, just to improve my understanding of
>> Cocoa, even if I decide against using this approach.
>
> Well, I have successfully posted mouse-up events in the (rare) situation I
> described, but I filled in all the parameters, so maybe Dave's suggestion is
> right - you have to make sure the event is well-formed. Many of its
> parameters are esoteric, but can be obtained from the objects around you -
> the window, current graphics context, and so on.
>
> If I haven't made my point yet however, don't pursue this - just do it
> correctlyâ„¢ instead. The above approach is what I use in DrawKit and it has
> never let me down ;-)
>
> --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