Notification for mouse-up on NSStepper

  • I'd like to get notified when the user releases the mouse after
    interacting with an auto-repeating NSStepper, so I can update my UI
    appropriately.

    However, this is challenging. There are no built-in notifications or
    delegate methods. I tried subclassing NSStepper and overriding -mouseUp:
    but this method is never called.

    I suspect (but haven't yet tested) that NSStepper overrides -mouseDown:
    and runs its own event loop while the mouse button is down, and consumes
    the mouse-up event. If that's the case, then I could override
    -mouseDown:, call [super mouseDown:event] and then once it returns, I
    know the mouse has been released and the stepper has already gone
    through all of its motions. However, I am not sure that this is a safe
    thing to rely on; I hate to rely on implementation details that could be
    subject to change.

    Is there a good way to do this that I haven't thought of?
  • On 6 Dec 2007, at 22:41, John Stiles wrote:

    > I suspect (but haven't yet tested) that NSStepper overrides -
    > mouseDown: and runs its own event loop while the mouse button is
    > down, and consumes the mouse-up event. If that's the case, then I
    > could override -mouseDown:, call [super mouseDown:event] and then
    > once it returns, I know the mouse has been released and the stepper
    > has already gone through all of its motions. However, I am not sure
    > that this is a safe thing to rely on; I hate to rely on
    > implementation details that could be subject to change.
    >
    > Is there a good way to do this that I haven't thought of?

    FWIW, I can't think of one, and I've also had to write code previously
    that relies on the system-provided classes using the tracking-loop
    approach to mouse tracking.

    I doubt Apple will change any class currently using a tracking-loop to
    use three-method tracking, but it would be nice to have some guarantee
    that any system-defined NSResponder subclass that currently tracks the
    mouse this way will *always* track the mouse this way, at least for
    applications built on currently shipping systems.

    It's a shame that there's no obvious way from code to distinguish
    between the two cases (i.e. tracking loop versus three-method approach).

    Kind regards,

    Alastair.

    --
    http://alastairs-place.net
  • On Dec 7, 2007, at 4:41 AM, Alastair Houghton wrote:

    > On 6 Dec 2007, at 22:41, John Stiles wrote:
    >
    >> I suspect (but haven't yet tested) that NSStepper overrides -
    >> mouseDown: and runs its own event loop while the mouse button is
    >> down, and consumes the mouse-up event. If that's the case, then I
    >> could override -mouseDown:, call [super mouseDown:event] and then
    >> once it returns, I know the mouse has been released and the stepper
    >> has already gone through all of its motions. However, I am not sure
    >> that this is a safe thing to rely on; I hate to rely on
    >> implementation details that could be subject to change.
    >>
    >> Is there a good way to do this that I haven't thought of?
    >
    > FWIW, I can't think of one, and I've also had to write code
    > previously that relies on the system-provided classes using the
    > tracking-loop approach to mouse tracking.
    >
    > I doubt Apple will change any class currently using a tracking-loop
    > to use three-method tracking, but it would be nice to have some
    > guarantee that any system-defined NSResponder subclass that
    > currently tracks the mouse this way will *always* track the mouse
    > this way, at least for applications built on currently shipping
    > systems.
    >
    > It's a shame that there's no obvious way from code to distinguish
    > between the two cases (i.e. tracking loop versus three-method
    > approach).

    You can do this:

    - (void) mouseDown: (NSEvent *) event
    {
    [super mouseDown: event];
    if ([[NSApp currentEvent] type] == NSLeftMouseUp) {
      [self doSomethingAfterTracking];
    }
    }
    - (void) mouseUp: (NSEvent *) event
    {
    [self doSomethingAfterTracking];
    }

    So if the super's mouseDown does the tracking loop, the current event
    will be the mouse up (that stopped the tracking loop).  If it doesn't,
    then mouseUp will be called like normal.

    Glenn Andreas                      <gandreas...>
      <http://www.gandreas.com/> wicked fun!
    quadrium | prime : build, mutate, evolve, animate : the next
    generation of fractal art
  • On 7 Dec 2007, at 14:56, glenn andreas wrote:

    > You can do this:
    >
    > - (void) mouseDown: (NSEvent *) event
    > {
    > [super mouseDown: event];
    > if ([[NSApp currentEvent] type] == NSLeftMouseUp) {
    > [self doSomethingAfterTracking];
    > }
    > }
    > - (void) mouseUp: (NSEvent *) event
    > {
    > [self doSomethingAfterTracking];
    > }
    >
    > So if the super's mouseDown does the tracking loop, the current
    > event will be the mouse up (that stopped the tracking loop).  If it
    > doesn't, then mouseUp will be called like normal.

    Hmmm.  I hadn't thought of checking -currentEvent.  If we assume that
    there are no guarantees from Apple, it's better than doing nothing,
    though I think it still isn't completely robust wrt changes in the
    superclass.

    Kind regards,

    Alastair.

    --
    http://alastairs-place.net
  • I ended up overriding -mouseDown: to call super's mouseDown and then did
    my update afterwards. And comments aplenty to explain the situation.
    It relies on implementation details, but I think Glenn's code also
    relies on (different) implementation details, so if it's six of one and
    a half dozen of the other, I'll take the more direct approach. ;)

    Alastair Houghton wrote:
    > On 7 Dec 2007, at 14:56, glenn andreas wrote:
    >
    >> You can do this:
    >>
    >> - (void) mouseDown: (NSEvent *) event
    >> {
    >> [super mouseDown: event];
    >> if ([[NSApp currentEvent] type] == NSLeftMouseUp) {
    >> [self doSomethingAfterTracking];
    >> }
    >> }
    >> - (void) mouseUp: (NSEvent *) event
    >> {
    >> [self doSomethingAfterTracking];
    >> }
    >>
    >> So if the super's mouseDown does the tracking loop, the current event
    >> will be the mouse up (that stopped the tracking loop).  If it
    >> doesn't, then mouseUp will be called like normal.
    >
    > Hmmm.  I hadn't thought of checking -currentEvent.  If we assume that
    > there are no guarantees from Apple, it's better than doing nothing,
    > though I think it still isn't completely robust wrt changes in the
    > superclass.
    >
    > Kind regards,
    >
    > Alastair.
    >
    > --
    > http://alastairs-place.net
    >
    >
  • It's not super-likely that 'mouse up' is exactly what you're after.
    Remember that the user can manipulate a stepper with the keyboard.
    Mouse down will never be called in that case, and if you do anything
    that specifically looks for a mouse up after receiving the action
    message, you won't see it.

    Is the issue that you have a cheap action that you want to do
    continuously as the user manipulates the slider, and an expensive
    action that you want to do when he is finished (e.g. low quality photo
    resize followed by high quality resize)?  For that, one possibility is
    to handle it at the controller layer by doing the expensive action on
    a delay, and always canceling and rescheduling when you receive an
    additional request.  This is typed in the browser, so it's probably
    slightly wrong, but something like this:

    - (void)takeActionSlightlyAsyncronously:(id)sender {
        [self takeCheapAction];
        [[self class] cancelPreviousPerformRequestsWithTarget:self
    selector:@selector(takeExpensiveAction) object:nil];
        [self performSelector:@selector(takeExpensiveAction)
    withObject:nil afterDelay:timeout];
    }

    -Ken

    On Dec 6, 2007 2:41 PM, John Stiles <JStiles...> wrote:
    > I'd like to get notified when the user releases the mouse after
    > interacting with an auto-repeating NSStepper, so I can update my UI
    > appropriately.
    >
    > However, this is challenging. There are no built-in notifications or
    > delegate methods. I tried subclassing NSStepper and overriding -mouseUp:
    > but this method is never called.
    >
    > I suspect (but haven't yet tested) that NSStepper overrides -mouseDown:
    > and runs its own event loop while the mouse button is down, and consumes
    > the mouse-up event. If that's the case, then I could override
    > -mouseDown:, call [super mouseDown:event] and then once it returns, I
    > know the mouse has been released and the stepper has already gone
    > through all of its motions. However, I am not sure that this is a safe
    > thing to rely on; I hate to rely on implementation details that could be
    > subject to change.
    >
    > Is there a good way to do this that I haven't thought of?
    >
previous month december 2007 next month
MTWTFSS
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31            
Go to today