Unexpected Pointer Changes

Hello,

I have a main window with a vertical split view. The NSSplitView has an NSOutlineView on the left and an empty space on the right. When the user clicks on a filename in the outline view, an NSTextView is created dynamically from a nib and displayed on the right side of the NSSplitView. The NSTextView controller also dynamically creates an NSRulerView in its viewDidLoad and adds the ruler to the NSTextView's NSScrollView.

Oddly, I've noticed that if I have the mouse pointer in the NSTextView's area and move it left into the NSRulerView's area, the pointer changes from an I-Beam to an arrow, as expected. If I move the mouse pointer from the NSOutlineView and to the right, over the NSSplitView divider and into the NSRulerView, the pointer immediately turns into an I-Beam when it reaches the NSRulerView.

I experimented a little bit with resetCursorRects with no luck. Anyone have any ideas how to get the expected behavior of the pointer to turning to an arrow when coming from the left... or even how to approach debugging this?

Thank you!

Accepted Reply

Based on a Stack Overflow question, I've managed to get it to work by doing this:

Code Block objective-c
- (void)mouseEntered:(NSEvent *)event {
    self.mouseInRect = YES;
}
- (void)mouseExited:(NSEvent *)event {
    self.mouseInRect = NO;
}
- (void)mouseMoved:(NSEvent *)event {
    [super mouseMoved:event];
    if (self.mouseInRect) {
        [[NSCursor arrowCursor] set];
    }
}

Replies

Do you set cursor somewhere in code or do you rely in automatic change ?
Please show all relevant code.

Sometimes, cursor management is a bit erratic and hard to fix.
Initially, I was relying on the automatic change. Then I added code to update the cursor. I get the same results either way. I am subclassing NSRulerView here. A plain old NSRulerView exhibits the same behavior though. Here's what I have...

First, setup tracking area:
Code Block objective-c
- (void)setFrame:(NSRect)frame {
     NSLog(@"setFrame");
    [super setFrame:frame];
    NSArray<NSTrackingArea *> *oldAreas = self.trackingAreas;
    NSLog(@"frame = %f, %f, %f, %f", self.frame.origin.x, self.frame.origin.y, self.frame.size.width, self.frame.size.height);
    if (oldAreas.count > 0) {
        NSLog(@"removing %li tracking areas", oldAreas.count);
        for (NSTrackingArea *ta in oldAreas) {
            [self removeTrackingArea:ta];
        }
    }
    self.mouseTrackingArea = [[NSTrackingArea alloc] initWithRect:self.frame
                                                          options:(NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveInKeyWindow | NSTrackingCursorUpdate)
                                                            owner:self
                                                         userInfo:nil];
    [self addTrackingArea:self.mouseTrackingArea];
}


Handle mouse events:
Code Block objective-c
- (void)mouseEntered:(NSEvent *)event {
    NSLog(@"mouse entered");
    [[NSCursor arrowCursor] set];
}
- (void)cursorUpdate:(NSEvent *)event {
    NSLog(@"cursorUpdate");
    [[NSCursor arrowCursor] set];
}

Interestingly, the text "mouse entered" displays as soon as I move the mouse pointer into the targeted area, but the cursor change doesn't have any effect. Only when I enter the area from the right side, do I get the "cursorUpdate" message and that cursor change DOES take effect.
Based on a Stack Overflow question, I've managed to get it to work by doing this:

Code Block objective-c
- (void)mouseEntered:(NSEvent *)event {
    self.mouseInRect = YES;
}
- (void)mouseExited:(NSEvent *)event {
    self.mouseInRect = NO;
}
- (void)mouseMoved:(NSEvent *)event {
    [super mouseMoved:event];
    if (self.mouseInRect) {
        [[NSCursor arrowCursor] set];
    }
}

I ended up using    
Code Block
override func cursorUpdate(with event: NSEvent) { }

to manage cursor change when inside.
And set the cursor to a plain arrow on mouseExit.