Changing the point at which mouseEnter() is called for a component

When the mouse cursor is dragged over the bottom edge of a component (in this case a DrawableButton), mouseEnter() is not called for that component as soon as the mouse cursor enters the component’s bounds, but rather when it overlaps by a few pixels.

This is illustrated below, where the left screenshot below shows the mouse entering the component, and the right component shows the position at which mouseEnter() is called. This is on macOS 10.14.

NotInside Inside

What is the reason for this behaviour? Is the OS reporting the cursor x, y position as the first pixel within the black part of the cursor? …or is something off with JUCE’s screen position scaling?

In any case, how are people dealing with this? I guess in the case of DrawableButton, the button can be given an e.g. 3px border to compensate, but this then makes sizing a pain and creates problems if you have adjacent buttons.


Isn’t that just where the hot-spot of the cursor is on macOS?
I think it’s the first black pixel. Have you tried this on other software?

Yes, I am experiencing the same problem.
I am dealing with it by adding a 3px border around the component.

Note though: Windows works differently. This problem only appears on OSX. In Windows the mouse is very precise. As soon as the first pixel of the mouse enters the component, a call to mouseEnter() is made (at least in Win 10).

1 Like

Isn’t that just where the hot-spot of the cursor is on macOS? I think it’s the first black pixel.

After a bit of closer investigation it seems like the default hot spot is actually somewhere in between the zeroth white pixel and the zeroth black pixel. I determined this with a small Swift app that reports NSEvent.mouseLocation. The cursor is at y=0 when the black portion is fully off the screen and the white tip is partially on. It moves past y=799 (800 screen height) as the tip nudges slightly off the screen. The effect becomes more obvious with a larger cursor size.

Have you tried this on other software?

Yes, and the mouse does seem to become active slightly sooner for native UI elements than it does for JUCE components. When you take into account things like shadows around native buttons etc, the difference becomes more subtle, and it’s possible these factors account for the perceived difference in behaviour.

I think the answer is probably to do as @Alatar suggests and make clickable area slightly bigger than the button graphic. This is reasonable practice anyway, and seems to be what Apple do.


Well my opinion is that if you’ve built a GUI in which moving the mouse by 1 pixel actually makes a noticeable difference to the way things work, then you badly need to rethink your UX design.


@jules you are right. And I think I wrongly framed this thread as being about when mouseEnter() is called when it’s actually a question of UI design.

Here’s where a JUCE button becomes clickable:


And here’s where a native macOS button becomes clickable:


My opinion is the bottom design is better, and it seems to be what users expect, which is what prompted me and others to post on this. It’s not really about mouse hotspots and components, it’s about how the visual representation of the UI relates to its behaviour.

Obviously this can be customised, but I think in JUCE the defaults would benefit from tweaking.

Sure, but you could style a button in any way you like, leaving as much border as you want between the rectangle and the edge of the button.

The OSX one probably isn’t done that way because of the mouse-over behaviour - it has a small drop-shadow so they probably just left a couple of pixels at the bottom to draw that, and a side-effect is that the mouse seems to enter it a bit lower.

I think the underlying problem in my case is that I’ve been using DrawableButton with DrawableButton::ImageStretched to make copying mockups easier (image size = button size), when in fact this is a mistake as DrawableButton::ImageStretched causes edgeIndent to be ignored.

A better approach would be to use DrawableButton::ImageFitted, then the image gets a default indent of 3px (or more if needed). It just means button bounds need to be edgeIndent larger than the required image size.

(Leaving this here in case anyone else goes through the same process!)

1 Like