NSButton mouseDown mouseUp behaving differently on enabled
Asked Answered
O

4

13

If the NSButton is not enabled, the mouseDown: and mouseUp: behave as expected (so when the mouse is pushed down, the mouseDown: is called, and when it is released, the mouseUp: is called)

However, if the NSButton IS enabled, than the mouseUp: doesn't get called at all, and mouseDown: is called AFTER the mouse has been released

- (void)mouseDown:(NSEvent *)theEvent {
    [super mouseDown:theEvent];
}

- (void)mouseUp:(NSEvent *)theEvent {
    [super mouseUp:theEvent];
}

Why is this behaving differently, and how can i force the correct behaviour (the same as when the button is NOT enabled)

Olag answered 13/3, 2014 at 20:4 Comment(0)
S
6

To cobble off of the previous if the desire is to create a custom button whose action method can distinguish down presses from releases, try adding the property isPressed to it, and the following code:

 (void)mouseDown:(NSEvent *)event
 {
    self.isPressed = true;  
    [super mouseDown:event];

    self.isPressed = false;

    [self.target performSelector:self.action withObject:self];
}

The custom button must be set to send actions per:

[self.button sendActionOn: NSLeftMouseDownMask | NSLeftMouseUpMask];

Without this, the action method is not called until the button is released.

In the action method, isPressed can be queried. e.g:

int state = (int)[sender isPressed];

An annoying but harmless "Feature" of this is that the action method is called twice when the button is released: once from inside NSButton with isPressed still true. (This should be ignored.) The second time from custom button's performSelector method, with isPressed false.

Any comments on if this is likely to work on future releases?

Substage answered 13/2, 2016 at 15:19 Comment(0)
C
16

if someone is still looking for this ... I don't know why but this works for me ...

- (void)mouseDown:(NSEvent *)theEvent {
    NSLog(@"mouse down!");
    [super mouseDown:theEvent];
    NSLog(@"mouse up!");
}
Cyperaceous answered 30/11, 2014 at 17:58 Comment(3)
How do you write [super mouseDown:theEvent] in Swift? I need to do the same but in Swift I am not getting the mouseDown event in my override fun mouseDown?Decimalize
@NealDavis in Swift you write it like this: super.mouseDown(theEvent)Audrieaudris
This is incorrect if you want to have the mouseUp functionality actually run when the mouse click is released. This just calls NSLog(@"mouse up!") on mouseDown.Maemaeander
E
11

The behavior is correct. Your expectation that all mouse-up events go through responder methods is mistaken.

When the button is enabled, the superclass implementation of -mouseDown: will run an internal event tracking loop to track the mouse movement and show the button as pressed so long as the mouse is within it and show it as not pressed when the mouse moves out. This internal event loop is what receives the NSLeftMouseUp event. It's never dispatched to a responder method.

See Cocoa Event Handling Guide: Handling Mouse Events – Handling Mouse Dragging Operations – The Mouse-Tracking Loop Approach.

Erase answered 13/3, 2014 at 21:11 Comment(2)
what is annoying me is the different behaviour when isEnabled is YES and NOOlag
When the button is disabled then it has no need to do the internal mouse-tracking loop.Erase
S
6

To cobble off of the previous if the desire is to create a custom button whose action method can distinguish down presses from releases, try adding the property isPressed to it, and the following code:

 (void)mouseDown:(NSEvent *)event
 {
    self.isPressed = true;  
    [super mouseDown:event];

    self.isPressed = false;

    [self.target performSelector:self.action withObject:self];
}

The custom button must be set to send actions per:

[self.button sendActionOn: NSLeftMouseDownMask | NSLeftMouseUpMask];

Without this, the action method is not called until the button is released.

In the action method, isPressed can be queried. e.g:

int state = (int)[sender isPressed];

An annoying but harmless "Feature" of this is that the action method is called twice when the button is released: once from inside NSButton with isPressed still true. (This should be ignored.) The second time from custom button's performSelector method, with isPressed false.

Any comments on if this is likely to work on future releases?

Substage answered 13/2, 2016 at 15:19 Comment(0)
O
1

Building off the others, here's a Swift working parallel to the Objective-C mouse-tracking loop in Apple's documentation for a subclass of NSButton. Our code is keeping the events rather than passing them to the superclass and its event loop.

override func mouseDown(theEvent: NSEvent) {
    var isInside: Bool = true
    Swift.print( "MyButton Mouse Down" )

    self.highlighted = isInside
    while true {
        let event = self.window?.nextEventMatchingMask(
            Int(NSEventMask.LeftMouseUpMask.union(.LeftMouseDraggedMask).rawValue))
        let mouseLoc = self.convertPoint((event?.locationInWindow)!, toView: nil)
        isInside = CGRectContainsPoint(self.bounds, mouseLoc)
        if event?.type == .LeftMouseDragged {
            self.highlighted = isInside
        }
        else if event?.type == .LeftMouseUp {
            self.highlighted = false
            if isInside {
                Swift.print( "MyButton Mouse Up" )
            }
            break
        }
    }
Outpoint answered 6/5, 2016 at 0:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.