Need the Why and How of mouseDragged:

  • I'm implementing drag and drop in a custom view (i.e., a subclass of
    NSView).  It works if I send
    dragImage:at:offset:eventpasteboard:source:slideBack: in mouseDown:,
    except that the only [event type] I ever receive in mouseDown: is
    NSLeftMouseDown.  So I can't distinguish clicks from drags.  If I set
    a drag image unconditionally, the image flashes on the screen if the
    user only clicks, which is annoying.

    It seems that mouseDragged: would be more sensible method from which
    to send dragImage:at:offset:eventpasteboard:source:slideBack:.  But I
    never get any mouseDragged:messages.  Reading the list archives, I get
    the impression that there might be some kind of trick to getting
    these, but I can't find the trick.

    My mouseDown: method does send -[super mouseDown:] at its conclusion.
    My custom view is a subview of an RBSplitSubview.

    Thanks for any tips,

    Jerry Krinock
  • On 13 Dec 2007, at 14:30, Jerry Krinock wrote:

    > I'm implementing drag and drop in a custom view (i.e., a subclass of
    > NSView).  It works if I send
    > dragImage:at:offset:eventpasteboard:source:slideBack: in mouseDown:,
    > except that the only [event type] I ever receive in mouseDown: is
    > NSLeftMouseDown.  So I can't distinguish clicks from drags.  If I
    > set a drag image unconditionally, the image flashes on the screen if
    > the user only clicks, which is annoying.
    >
    > It seems that mouseDragged: would be more sensible method from which
    > to send dragImage:at:offset:eventpasteboard:source:slideBack:.  But
    > I never get any mouseDragged:messages.  Reading the list archives, I
    > get the impression that there might be some kind of trick to getting
    > these, but I can't find the trick.

    There's no trick here.  The problem you're seeing is most likely that
    RBSplitView is running a mouse tracking loop when it gets -mouseDown:,
    which is eating all the -mouseDragged events.

    Unfortunately, if a view uses this style of mouse tracking, it's
    difficult to add additional code to do something different (unless the
    view has adequate hooks built into it) without modifying the original
    source code or re-implementing the existing behaviour.

    It would be worth considering whether you should be implementing your
    drag & drop on the RBSplitView subclass itself or whether your code
    really belongs in a child view of the RBSplitView.

    Kind regards,

    Alastair.

    --
    http://alastairs-place.net
  • On Thursday, December 13, 2007, at 07:35AM, "Alastair Houghton" <alastair...> wrote:
    > On 13 Dec 2007, at 14:30, Jerry Krinock wrote:
    >
    >> It seems that mouseDragged: would be more sensible method from which
    >> to send dragImage:at:offset:eventpasteboard:source:slideBack:.  But
    >> I never get any mouseDragged:messages.  Reading the list archives, I
    >> get the impression that there might be some kind of trick to getting
    >> these, but I can't find the trick.
    >
    > There's no trick here.  The problem you're seeing is most likely that
    > RBSplitView is running a mouse tracking loop when it gets -mouseDown:,
    > which is eating all the -mouseDragged events.
    >
    > Unfortunately, if a view uses this style of mouse tracking, it's
    > difficult to add additional code to do something different (unless the
    > view has adequate hooks built into it) without modifying the original
    > source code or re-implementing the existing behaviour.

    I posted about this same problem a month or two ago, though with NSSplitView.  It's odd that a container view that's useless without subviews would break mouse tracking for its subviews.  Anyway, my fix was the following in an NSSplitView subclass, so maybe the same would work for RBSplitView?  Corrections welcome.

    - (void)mouseDown:(NSEvent *)theEvent {
        BOOL inDivider = NO;
        NSPoint mouseLoc = [self convertPoint:[theEvent locationInWindow] fromView:nil];
        NSArray *subviews = [self subviews];
        int i, count = [subviews count];
        id view;
        NSRect divRect;

        for (i = 0; i < count - 1; i++) {
            view = [subviews objectAtIndex:i];
            divRect = [view frame];
            if ([self isVertical]) {
                divRect.origin.x = NSMaxX(divRect);
                divRect.size.width = [self dividerThickness];
            } else {
                divRect.origin.y = NSMaxY(divRect);
                divRect.size.height = [self dividerThickness];
            }

            if (NSPointInRect(mouseLoc, divRect)) {
                inDivider = YES;
                break;
            }
        }

        if (inDivider) {
            [super mouseDown:theEvent];
        } else {
            [[self nextResponder] mouseDown:theEvent];
        }
    }
  • Thanks to Alastair and to Adam for the code.  But I now have seen
    exactly the same behavior in another instance of this same custom
    control(*), which is not in an RBSplitView.  So it looks like
    RBSplitView is not my problem, at least not yet.

    I have found that I get -mouseDragged: if either:

          (1) I do NOT invoke super in my -mouseDown: implementation
      or  (2) I send it -setEnabled:NO

    So, using the above knowledge I can get the behavior I want.  But it
    makes absolutely no sense.  In particular, -setEnabled documentation
    implies the opposite effect.  But I've now repeated these experiments
    many times.  Also, I do not use the value of -isEnabled in my custom
    control implementation.  So this does not appear to be a side-effect
    of other code.

    From this code:

    - (void)mouseDragged:(NSEvent *)event {
        NSLog(@"I have received mouseDragged") ;
        NSLog(@"My enabled state is: %d", [self isEnabled]) ;
        ...
    }

    I get this console log:

    2007-12-13 09:21:45.841 Bookdog[10173:10b] I have received mouseDragged
    2007-12-13 09:21:45.842 Bookdog[10173:10b] My enabled state is: 0

    From -[NSControl setEnabled:] documentation...

    "YES if you want the receiver to react to mouse events; otherwise, NO."

    The logged result seems contrary to documentation.  Can anyone explain
    this?  I'm worried that I'm coding myself into a dark alley.

    Jerry Krinock

    (*) In my original post, I called this a custom view, an NSView*
    subclass.  More specifically, it is a custom control, an NSControl*
    subclass.
  • On 13 Dec 2007, at 17:37, Jerry Krinock wrote:

    > Thanks to Alastair and to Adam for the code.  But I now have seen
    > exactly the same behavior in another instance of this same custom
    > control(*), which is not in an RBSplitView.  So it looks like
    > RBSplitView is not my problem, at least not yet.

    No, sorry.  I seem to be having trouble reading things properly
    today.  I thought you'd subclassed RBSplitView, but you're using a
    subview.

    The problem is the superclass of your view (which I assumed was
    RBSplitView, but of course it isn't), which is doing what I said (i.e.
    it has a mouse tracking loop that runs when you send -mouseDown:).

    The solution is to do one of the following:

    1. Add some hooks to the mouse tracking loop in the superview's code, or

    2. Copy the superview's code for -mouseDown: into the subview and
    customize it appropriately, or

    3. Change the superview to use three-method tracking.

    If you don't have the code for the superview, and it doesn't have
    sufficient hooks, it's a bit of a nuisance but you can often re-
    implement the mouse tracking in the subview (depending on what it does).

    Kind regards,

    Alastair.

    --
    http://alastairs-place.net
  • On 2007 Dec, 13, at 9:47, Alastair Houghton wrote:

    > If you don't have the code for the superview, and it doesn't have
    > sufficient hooks, it's a bit of a nuisance but you can often re-
    > implement the mouse tracking in the subview (depending on what it
    > does).

    Thanks again, Alastair.  I sure don't have the code for the
    superview.  In my second instance, the superview is NSView, the
    content view of an NSWindow.

    Let me summarize my previous message in fewer words:  I "solved" it,
    but the solution doesn't make sense.

    To get mouseDragged:, I must  either setEnabled:NO or ^not^ invoke
    super in mouseDown:.

    How could setEnabled:YES cause me to not get mouseDragged: ?

    Jerry
  • On 13 Dec 2007, at 18:21, Jerry Krinock <jerry...> wrote:

    > Thanks again, Alastair.  I sure don't have the code for the
    > superview.  In my second instance, the superview is NSView, the
    > content view of an NSWindow.

    [snip]

    > To get mouseDragged:, I must  either setEnabled:NO or ^not^ invoke
    > super in mouseDown:.
    >
    > How could setEnabled:YES cause me to not get mouseDragged: ?

    If your superclass is NSView, you don't need to forward the message to
    super.

    As for the -setEnabled: thing, the default behaviour is probably to
    track the mouse until mouse up, but only if -enabled is YES. I can't
    say I've noticed this before myself, because I don't tend to forward -
    mouseDown: to super where super is a plain NSView.

    Kind regards,

    Alastair

    --
    http://alastairs-place.net
  • On 2007 Dec, 13, at 11:09, Alastair Houghton wrote:

    > If your superclass is NSView, you don't need to forward the message
    > to super.
    >
    > As for the -setEnabled: thing, the default behaviour is probably to
    > track the mouse until mouse up, but only if -enabled is YES. I can't
    > say I've noticed this before myself, because I don't tend to forward
    > -mouseDown: to super where super is a plain NSView.

    Thanks, Alastair.  Your explanation makes sense.

    Since my object is specifically an NSControl (subclass of NSView), I
    had been doing forwarding mouseDown: to super in order to get an
    action sent.  The solution I used is to replace

      [super mouseDown:event] ;

    with

      [self sendAction:[self action] to:[self target]] ;

    All seems to be working fine now.
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