+3  A: 

What about overriding the NSResponder event methods (mouseUp:, mouseDown:, etc) in your custom buttons (presumably you'd be subclassing NSControl or NSButton to make these)? In those methods you could properly calculate your bounding rectangle (or circle, in this case) and do a simple hit test with the coordinates of the click to see if the click should be handled.

You might also be able to find some help in Apple's event handling docs, specifically about mouse events and custom views.

Marc W
Thanks, that would be a possibility for the center button, but with the outside buttons that would be more difficult and require a bunch of bounds-checking code... what if I change my button's positions for some reason? (this is not a personal project so this may be subject to change unfortunately) Then I'd have to re-write most of my bounding check code... Would there be a way to do this with some sort of mask? Say "let the click pass through if the bitmap is transparent at this point"?
Form
I don't think you can do it with a mask like you are thinking (transparency detection, etc). These are pretty basic shapes (partial circles for the outer buttons underneath a full circle for the inner). You can access the controls' current coordinates and dimensions in code and use those to calculate the hit bounds so you aren't hardcoding anything. Use the fact that the center button is on top to your advantage.
Marc W
It is possible to use transparency (alpha) detection and it works very well and although the method I'm using for now is 10.4+ there is a way to do this using something else than colorAtX:y: as long as you have access to the bitmapRep, like mahboudz pointed out.
Form
+2  A: 

What about making it all a single control? It might make drawing a little bit complex, but it looks like you're doing custom drawing anyway.

If you did it as a single control, you could first check and see if the click point is within the center circle. If it's not, then all you have to do is identify which quadrant it's in to know which "button" it belongs to (while also verifying that the click fits within the outer circle).

Alternatively, you could create a transparent view over your buttons that captures the click events and forwards them on to the appropriate control based on the logic I just specified.

Dave DeLong
+1  A: 

Similar to Marc W's response, in your own event method you can check the alpha value of the clicked on the bitmap and ignore if the alpha is lower than a certain value.

To get the bitmap, read this.

Now you should have a bitmap pointer like this (pseudocode - you'll have to fill in the pieces):

pixels = CGBitmapContextGetData( ctx ); // there's actually two ways to get pixels in the above Apple tech note

Then, you can do this to get the pixel you are interested in, and test it:

// I'm assuming each pixel is 24 bits of color and one byte of alpha
#define getRed(p) ((p) & 0x000000FF)
#define getGreen(p) ((p) & 0x0000FF00) >> 8
#define getBlue(p) ((p) & 0x00FF0000) >> 16
#define getAlpha(p) ((p) & 0xFF000000) >> 24

CGPoint pixPt;
long pixel;
pixPt = getMouseClick();

pixel = pixels[pixPt.x + pixPt.y * bytesPerRow];

if (getAlpha(pixel) < 25)
  // you've clicked on transparent pixels, alpha of 10% or less (alpha ranges from 0-255)

This opens up some possibilities for you, such as having a ring around your inside circle that is inert and won't belong to any of the quadrants or the middle circle. Of course, there's always other ways to do these things without resorting to pixel-wrangling :-)

mahboudz
That seems more like what I'd like to do, that way if the shape of my control changes it should adjust automatically. Is there a method for checking the opacity of an image at a specific point in a button's image? (I think it would be less work to subclass an NSButton instead of subclassing NSView and drawing manually, but if it's not possible to check for alpha with a button I'll most probably go with the NSView).
Form
I'm adding bitmap handling info back into my response.
mahboudz
Thanks a lot, I'll see what I can do with that!
Form
Although I now use the Cocoa API for alpha detection, the fact that you mentioned the bitmap context pointed me in the right direction. I'll also be looking into your code when I need to support older than 10.4 but for now colorAtX:y: on the NSBitmapImageRep will work. Thanks for the help!
Form
I didn't even know about colorAtX!
mahboudz
+1  A: 

If you generate the shapes in code using NSBezierPath, testing whether a click is inside one of the shapes is a one-liner: send the shape a containsPoint: message.

I'd make this a single view that owns the five paths. When drawing, draw the center one last; when responding to a mouse event, hit-test it first. Give this view one action property per segment, and optionally one target per segment as well, depending on what you need.

Peter Hosey
Your solution is very interesting, I didn't know you could hit test NSBezierPaths like this. It will surely come handy. But in my case (with the shapes changing being non-negligible propability) I think it would be a lot of re-write if I ever change something. I'll try the bitmap alpha-testing first and if that does not work as I hope it does I'll try that. Thanks for taking the time to answer my question!
Form