On 03/02/2010, at 2:12 AM, Eric Gorr wrote:

> This method is generally not overridden



> I have filed a bug requesting that trackMouse:inRect:ofView:untilMouseUp: 
> call continueTracking:at:inView: and stopTracking:at:inView:mouseIsUp: ... or 
> is there a reason why it shouldn't?


Note that it says is GENERALLY not overridden, not that it is NEVER overridden.

I've noticed that quite a few built-in cell classes override at this level, 
where others override the more fine-grained methods. I guess it's just a 
question of what makes the most sense or is easiest for a given implementation. 
NSButtonCell does call -startTrackingAt:inView:, but not the others, I think. 
It appears to run a modal loop within -startTrackingAt:...

I've only written one or two custom cell classes but have overridden at both 
levels. I did find that for 'button-like' tracking behaviour, overriding 
-trackMouse:... or -startTrackingAt:... was simplest, because you can keep 
control in an event-fetching loop until the mouse goes up before returning. For 
many controls, that's way easier than dealing with three other methods called 
at different times.

Back to your original question, you want to show a menu after a delay. I do 
this in a custom button cell (subclass of NSButtonCell). Here's my code FWIW, 
which unfortunately is a bit hackish. I don't bother cancelling the timer on 
mouse up, but I decide whether to actually show the menu based on whether the 
cell is still highlighted at the time, otherwise the timer callback does 
nothing. I also found it necessary to "fake up" a mouse up event when the menu 
tracking has completed to ensure the cell tracking is forced to return, because 
otherwise the pop-up menu tracking has swallowed the mouse-up event and so the 
button would otherwise remain stuck in its tracking loop waiting for a mouse-up 
that will never arrive. There may well be a far less hackish way to do that, 
but this does work:

(I note that KBPopUpToolbarItem doesn't do something similar because it 
implements its own tracking loop in its entirety, so has full control over when 
it returns. My code relies on inheriting more of NSButtonCell's standard 
behaviour, so the mouse-up event was needed.)



- (BOOL)        startTrackingAt:(NSPoint) startPoint inView:(NSView*) 
controlView
{
        // if there is a menu and show after delay is YES, start a timer for 
showing the menu
        
        if([self menu] && [self showsMenuAfterDelay] && mMenuTimer == nil)
        {
                mMenuTimer = [[NSTimer timerWithTimeInterval:[self menuDelay] 
target:self selector:@selector(menuDelayCallback:) userInfo:nil repeats:NO] 
retain];              
                
                [[NSRunLoop currentRunLoop] addTimer:mMenuTimer 
forMode:NSEventTrackingRunLoopMode];
                [[NSRunLoop currentRunLoop] addTimer:mMenuTimer 
forMode:NSDefaultRunLoopMode];
                
                // make a note of the mouse location for faking the mouse-up at 
the end:

                mMouseDownPoint = [controlView convertPoint:startPoint 
toView:nil];
        }
        
        return [super startTrackingAt:startPoint inView:controlView];
}


- (void)                menuDelayCallback:(NSTimer*) timer
{
#pragma unused(timer)
        
        // show the cell's menu if the button is still highlighted (if user let 
go, it won't be)
        
        if([self isHighlighted])
        {
                // pop up the menu and switch tracking to it.
                
                NSMenu* menu = [self menu];
                NSEvent* event = [NSApp currentEvent];
                
                // menu tracking keeps control until a choice is made:

                [NSMenu popUpContextMenu:menu withEvent:event forView:[self 
controlView]];

                // on return, post a mouse-up so that the cell tracking 
completes as normal. The mouse location
                // was recorded at the time it went down and the callback was 
scheduled

                NSWindow* window = [[self controlView] window];

                NSEvent* fakeMouseUp = [NSEvent mouseEventWithType:NSLeftMouseUp
                                                        location:mMouseDownPoint
                                                        modifierFlags:0
                                                        timestamp:[NSDate 
timeIntervalSinceReferenceDate]
                                                        windowNumber:[window 
windowNumber]
                                                        
context:[NSGraphicsContext currentContext]
                                                        eventNumber:0
                                                        clickCount:1
                                                        pressure:0.0];
                
                [window postEvent:fakeMouseUp atStart:YES];
                
                // after a menu selection, ensure the button is set to its ON 
state:

                [(NSControl*)[self controlView] selectCell:self];
                [self setState:NSOnState];
        }
        
        [mMenuTimer release];
        mMenuTimer = nil;
}



--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