mouseExit called almost immediately after mouseEnter for Combo box

I just discovered that mouseExit is called almost immediately after mouseEnter for Combo box, except the down arrow bit.
I found this while implementing an on screen hover help type thing. I’m using commit f90065a483d8fb82dcba38a4e5b983867a00072c of develop from 17th Feb.

OK, I got around this problem by using a high res timer, and counting the milliseconds - so if the time between enter and exit is very short then ignore the exit. Also forcing exit when entering the background component sorted this issue out. It’s not ideal, but it works.
BTW this also goes wrong when the combo box is the only child of the editor.

This sounds a bit convoluted.
Wouldn’t a MouseListener attached to the combo box which simply checks if the mouse is over the combo box or any of its children be simpler?

Just do that check for any mouseEnter/Exit call and you should have the information you need?

Thanks for replying. I tried many ways, all my controls work as expected with the Enter/Exit functions, apart from the combobox. Which means it’s a Juce thing. Never mind, the work around seems to be fine.

But I think what you’re seeing is expected as ComboBox contains a Label and maybe other components. So the mouseEnter/Exit behaviour you’re seeing is expected.
My suggestion was how you handle these cases.


Are you actually using a juce::HighResolutionTimer in your code?

As soon as I enter the combo box, it sends an exit almost immediately. That can’t be right surely? I even check the component name and a DBG print confirms it.

OK If I add a mouse listener to just the control WITHOUT nested events enabled, it still send an exit event immediately after entering the component.

What I’m getting at is that ComboBox is probably made up of several internal components. The mouse might enter the ComboBox but if it also enters a component that the ComboBox contains, then you’ll get and exit call for the ComboBox.
Surely you can see what’s triggering the mouseExit call?


I’ve not actually tried to replicate this but I know this kind of thing appears in lots of situations and this is how I’ve always dealt with it.

I just doubt that there’s a juce bug that means mouse callbacks happen correctly for all components except ComboBox…


Perhaps a better approach would be to say what you’re actually trying to do? And what area of the ComboBox you’re actually interested in as that will determine how you approach the problem.

I’ll just have so do you a short demo to show you the problem. It only works for the down arrow part of the box, but of course the whole thing is clickable.

Here you go, it’s a really simple test that displays some text when you mouse over the combobox.
But it only works with the down arrow bit.
I’m using windows 10, with a “GUI application” starter. I hope it’s all there…
http://quikquak.com/Temp/ComboBoxBug.zip

Like Dave said, ComboBox’s contain a child Component - a Label - to display the current text. The arrow is drawn directly onto the ComboBox, not a child.

When the mouse enters the bounds of the ComboBox is calls mouseEnter. However the mouse will almost immediately enter the bounds of the Label. Even though the Label is a child of the ComboBox, it is still a different Component so when the mouse enters the Label it exists the ComboBox.

In your mouseExit function, you can make a call to Component::isMouseOverOrDragging() which takes a boolean as an argument to say whether to return true if the mouse is over any of the Component’s children. If the ComboBox has triggered a mouseExit call but the mouse is still over the box or one of its children, you can just ignore the call.

//...
void MainComponent::mouseExit(const MouseEvent&e)
{
	String name = e.eventComponent->getName();
	DBG("EXIT " + name);

    if (!categoryCombo.isMouseOverOrDragging(true))
		sometext.setVisible(false);
}
//...
1 Like

Oops I missed something - since your MainComponent won’t receive mouseExit calls from the ComboBox’s Label, the text won’t dissappear properly.

You also need to add the MouseListener to all of the ComboBox’s nested children, using the second argument of Component::addMouseListener():

// ...
categoryCombo.addMouseListener(this, true);
// ...

OK I had to also add this to mouseEnter for it to work, because if the mouse enters too fast it doesn’t work:

	if (categoryCombo.isMouseOverOrDragging(true))
		sometext.setVisible(true);

And then we’re all good.
I wonder how this bug can be fixed properly? I’m guessing it’s unique to ComboBox.
Thanks for help Im_Jimmi
edit Actually I only needed the mouse enter one in the end.

This isn’t a big, you’re just making an assumption that ComboBox doesn’t have any children and that assumption is incorrect.

If what you really mean is “is the mouse over the ComboBox or any of its children”, then there is a method for exactly that as has been explained here.

OK, if I want a COMPONENT to register Enter and Exit for the said component’s entire bounding box then it’s a bug if it doesn’t work as expected. It doesn’t matter how complex the reason is.

Called when the mouse moves out of a component.

This will be called when the mouse moves off the edge of this component.

If the mouse button was pressed, and it was then dragged off the edge of the component and released, then this callback will happen when the button is released, after the mouseUp callback.

The documentation only mentions mouseExit being called when the mouse moves off of the edge of the component. However, however mouseExit has been called entering child components for as long as I can remember. I think changing it now would break a lot of code. At least the documentation should be updated to match the implemented behaviour.

Thanks, so why doesn’t turning off ‘wantsEventsForAllNestedChildComponents’ help at all?
(see my demo)

I’d do it like this:

	categoryCombo.addMouseListener(this, true);

void MainComponent::mouseEnter(const MouseEvent&e)
{
	String name = e.eventComponent->getName();
	DBG("ENTER " + name);

    auto le = e.getEventRelativeTo (this);
    if (categoryCombo.getBounds().contains (le.getPosition()))
	{
		sometext.setVisible(true);
	}
}

void MainComponent::mouseExit(const MouseEvent&e)
{
	String name = e.eventComponent->getName();
	DBG("EXIT " + name);

    auto le = e.getEventRelativeTo (this);
	if (! categoryCombo.getBounds().contains (le.getPosition()))
	{
		sometext.setVisible(false);
	}
}
1 Like

Excellent :grinning: