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: Graham Cox <email@hidden>
- Date: Wed, 4 Nov 2009 00:09:49 +1100
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