Search code examples
macoscocoansbutton

NSButton with Mouse Down/Up and Key Down/Up


I need a NSButton that gives me 2 events, one when the button is pressed down (NSOnState) and one when the button is released (NSOffState) and so far i've got it working with the mouse (intercepting the mouseDown: event). But using a keyboard shortcut doesn't work, it fires a NSOnState once and then after a delay really often. Is there any way to get a button that fires NSOnState when pressed and NSOffState when released?

My current subclass of NSButton looks like this and unfortunately works using a delegate:

-(void)awakeFromNib {
    [self setTarget:self];
    [self setAction:@selector(buttonAction:)];
}

-(void)mouseDown:(NSEvent *)theEvent {
    [_delegate button:self isPressed:YES];
    [super mouseDown:theEvent];
}

-(void)buttonAction:(id)sender {
    [_delegate button:self isPressed:NO];
}

Solution

  • The button can be set to send mouse events at both times by using -sendActionOn::

    [self.button sendActionOn: NSLeftMouseDownMask | NSLeftMouseUpMask];
    

    Handling keyboard events similarly seems more difficult. If you don't need the event exactly at the same time the highlight is removed from the button, you could override NSButton's -performKeyEquivalent: so that it will e.g. send the action twice.

    - (BOOL) performKeyEquivalent: (NSEvent *) anEvent
    {
        if ([super performKeyEquivalent: anEvent])
        {
            [self sendAction: self.action to: self.target];
            return YES;
        }
        return NO;
    }
    

    If you do need the event at the same time, I think you need to use a custom button cell (by creating a subclass of NSButtonCell and setting the button's cell in the initializer) and override its -highlight:withFrame:inView::

    - (void)highlight:(BOOL)flag
            withFrame:(NSRect)cellFrame
               inView:(NSView *)controlView
    {
        [super highlight: flag withFrame:cellFrame inView:controlView];
    
        if (flag)
        {
            // Action hasn't been sent yet.
        }
        else
        {
            // Action has been sent.
        }
    }