Question

I'm trying to implement a color picker in my Cocoa app. (Yes, I know about NSColorPanel. I don't like it very much. The point of rolling my own is that I think I can do better.)

Here's a picture of the current state of my picker.

Custom viewness!
(source: ryanballantyne.name)

The wells surrounding the color wheel are NSColorWell subclasses. They are instantiated programmatically and added to the color wheel view (an NSView subclass) by calling addSubView on the color wheel class.

I want to make it so that you can drag the color wells around by their grab handles. The start of that journey is making the cursor change to an open hand when the mouse hovers over the handles. Sadly, I can't use a cursor rect for this because most of my views are rotated. I must therefore use mouseMoved events and do the hit detection myself.

Here's the mouse event code I'm trying to make work:

- (void)mouseMoved:(NSEvent*)event
{
    NSLog(@"I am over here!\n");

    [super mouseMoved:event];

    NSPoint eventPoint = [self convertPoint:[event locationInWindow] fromView:nil];
    BOOL isInHandle = [grabHandle containsPoint:eventPoint];
    if (isInHandle && [NSCursor currentCursor] != [NSCursor openHandCursor])  {
        [[NSCursor openHandCursor] push];
    }
    else if (!isInHandle)  [NSCursor pop];
}

- (void)mouseEntered:(NSEvent*)event
{
    [[self window] setAcceptsMouseMovedEvents:YES];
}
- (void)mouseExited:(NSEvent*)event
{
    [[self window] setAcceptsMouseMovedEvents:NO];
    [NSCursor pop];
}

- (BOOL)acceptsFirstResponder
{
    return YES;
}
- (BOOL)resignFirstResponder
{
    return YES;
}

I find that my mouseMoved method is never called. Ditto for entered and exited. However, when I implement mouseDown, that one does get called, so at least some events are getting to me, just not the ones I want.

Any ideas? Thanks!

Was it helpful?

Solution

mouseEntered: and mouseExited: don't track entering/exiting your view directly; they track entering/exiting any tracking areas you've established in your view. The relevant methods are -addTrackingRect:owner:userData:assumeInside: and -removeTrackingRect:. Just pass [self bounds] for the first parameter if you want your whole view to be tracked. If your app is 10.5+ only, you should probably use NSTrackingArea instead as it directly supports getting mouse-moved events only inside the tracking area.

Keep in mind that 1) tracking rects have the same somewhat odd behavior as cursor rects w/r/t rotated views, and 2) if your bounds change (not merely your frame) you'll probably need to re-establish your tracking rect, so save the tracking rect's tag to remove it later.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top