(Targeting OS X 10.10, latest version of Xcode)
I have an NSButton subclass.
The button needs to respond to mouse rollovers/hovers, ala a web button.
The button requires an appearance in the shape of a circle. Accordingly, -setImage: and -setAlternateImage: are called with circular-shaped graphics.
Problem 1: the default behaviour of NSButton results in a positive hit-test when the mouse is clicked anywhere within the rect of the NSButton. This includes mouse clicks that are outside the circular region of the button.
Proposed solution: override -hitTest: to calculate distance of click from centre of the button. If distance > button's radius, we don't have a valid click.
OK, fine. That provides us with accurate collision detection on mouse clicks on circular buttons.
Problem 2: how to handle mouse hover/rollover, and changing the appearance of the button accordingly.
Proposed solution: add an NSTrackingArea to the button, and then override -mouseEntered: and -mouseExited: so we can change the button image and alternateImage as the mouse moves over the button.
OK, fine. That works for rectangles.
But... NSTrackingArea works on rectangles, not arbitrary regions such a circle. Worse, it does NOT honour -hitTest:
Proposed solution: add the circular collision detection code in to -mouseEntered: and -mouseExited:
But...
-mouseEntered: and -mouseExited: are only called ONCE per button entry/exit, not continuously. This means that if the mouse is moved just in to the button's rectangular region, but not far enough that it enters the button's circular region, -mouseEntered: will be called. But it will not be called again, until after -mouseExited: is called. Further movement of the mouse in to the circular region will have no effect. (No rollover will occur.)
i.e. it is not possible to continuously update the button's state with accurate mouse position information using these two methods.
QUESTION: does anyone know how to implement hover and pressing on a non-rectangular button?
[edit - SOLVED thanks to cacau.]
Solution steps:
Add an NSTrackingArea to the button, with (at a minimum) the NSTrackingMouseEnteredAndExited and NSTrackingMouseMoved options.
Implement -hitTest: in order to do accurate (e.g. circular) hit testing for button presses. We do not change the appearance of the button here.
Implement -mouseEntered: and -mouseExited:
Do (circular) collision detection within -mouseEntered, changing the appearance of the button accordingly via -setImage: (or otherwise flagging changes to your drawing strategy.)
On -mouseExited: set the button's appearance back to its default.
But, -mouseEntered: and -mouseExited: are not called continuously. Therefore we also need to implement -mouseMoved:
But, we must call -setAcceptsMouseMovedEvents:YES on the window, so that the -mouseMoved: method is called on the button.
Add the (circular) collision detection and appearance changing to -mouseMoved as well.