BUG: MouseListener::mouseExit doesn't get called if mouse moves quickly between components

To solve my “hover should highlight listbox item” problem, I added a mouselistener to a custom-class and it works really well.

If I move the mouse really quickly from one ListBox to the next, the hover in the “old” ListBox stays active. I’ve put some DBG macros in, and mouseExit never gets called, except when moving slower.

Have you seen this thread? Tooltips sometimes drawing out in left field

We were seeing similar issues which have now been fixed on the latest develop branch. Specifically, as of this commit: https://github.com/WeAreROLI/JUCE/commit/7d98da98ea4ea811ca5b0bc4ca315b95d9bfc0d1

That commit talks about a special case when the mouse leaves the plugin window. Not sure if it would fix it as well? I don’t really want to use the latest develop, as I prefer my frameworks a bit more mature. As long as it’s on the radar and gets fixed within the next few days, I’m happy.

This is the function that will send a mouse-enter:

And as you can see, that code should make it impossible to send a mouseEnter without giving the previously-over component a mouseExit (unless the previous one got deleted). So I think we’d want to see some sample code that reproduces your problem before investigating, as I suspect it might be something else that you’re misinterpreting rather than it dropping an event callback.

I can see that two lines were added about a month ago. So this is only in develop, not the current release?

Ah, didn’t realise it was a recent change. If so then maybe it’s a bug that was fixed recently - it’s always worth trying develop if you hit a problem, in case we’ve already fixed it.

I’ve replaced the juce_MouseInputSource.cpp and juce_MouseInputSource.h with the latest develop and still get the problem.

I’ll will prepare a small test-project demonstrating the problem and send it to you. It could be a few weeks as this is a not a critical problem and I’m quite busy myself.

Thanks for looking into it.

OK. I think I know what the problem is:

I’ve overwritten the hitTest function of the ListBox, so when the mouse is on the scrollbar, it doesn’t count as hovering over an item. If I remove that hitTest function, the problem goes away.

So it seems that the mouseEnter/mouseExit combination is somehow using the hitTest function to determine which Component is getting the mouseExit event. Is that correct?

Well, it uses hitTest to figure out which component the mouse is over for a given position. If your hitTest behaves inconsistently then yes, that could lead to messages getting screwed up.

So in my case, if the hitTest determines that the mouse is no longer over that component, shouldn’t it still call “mouseExit” then?

Isn’t that what the hitTest is designed to do?

Well, it depends exactly what you mean by that. Its purpose is just to handle components with unusual shapes - if you make it always return a consistent value for a given coordinate then nothing should go wrong.

But if you were to do something silly like make its return value depend on other external factors like the state of the mouse, or focus, or other components, then all bets are off.

It simply checks for the existence of the scrollbar and if the scrollbar is visible and the x-coordinate is outside the list display, but over the scrollbar, then it returns false.

Otherwise it is simply “return ListBox::hitTest (x, y);”

Can’t you just turn off the scrollbars if you don’t want them?

I guess that if you remove your hitTest override and the thing you’re complaining about stops happening, then it must be something you’re doing in there that’s messing stuff up.

They turn themselves on and off, depending on how much content is in the viewport. It’s a standard ListBox. Outside my control. I wish the viewport had a “setScrollbarAlwaysOn (bool horizontal, bool vertical)”, so they simply stayed on and the width of the actual ListBox content wouldn’t fluctuate, but that’s outside my control.

I can post the hitTest code here later (I’m on mobile right now), but shouldn’t mouseExit get called no matter what, as soon as hitTest for the component returns false and it returned true before?

Does this help:

ListBox::getVerticalScrollBar()->setAutoHide (false);
ListBox::getHorizontalScrollBar()->setAutoHide (false);
1 Like

Did you try my suggestion of removing your hitTest code and seeing what effect that has? It would seem sensible to find out whether the hit test is actually involved before spending more time discussing how it works.

Well yes, you can see in the code above how that’s enforced. Like I said earlier, the only way it would lose an event would be if the old component was deleted. And if you have an external listener that’s listening to all mouse events in a hierarchy, then you could expect it to see a mouse enter without an exit if a component was deleted while the mouse was over it. Maybe that happens for one of the subcomponents in your listbox.

Yes, I’ve removed the hitTest code and it works as it should. But now I get hover mouseHovers when the mouse is over the scrollbar:

Here is my hitTest code:

bool GUI_Listbox::hitTest (int x, int y)
	if (getViewport ()->getVerticalScrollBar ()->isVisible ())
		const int scrW = getViewport ()->getScrollBarThickness ();
		const int margin = scrW + getOutlineThickness () * 2;
		if (x >= (getWidth () - margin) && x < (getWidth () - (margin - scrW)))
			return false;

	return ListBox::hitTest (x, y);

As you can see, there is nothing nefarious going on.

No, no components are deleted or being added or anything like that. It’s simply two ListBoxes side by side, both with lots of entries in them (hence the scrollbar turning on) and when I move the mouse quickly between the left and right control (from the left to the right), there is a very good chance (>10%) that “mouseExit” doesn’t get called for the left control.

If I only leave the “return ListBox:hitTest (x, y);” in there and comment the rest out, I can’t reproduce the problem at all.

Thanks :slight_smile:

Well, the scrollbar’s visibility will get changed by some callback that depends on which component the mouse is over and you’re using that in deciding whether the mouse is over this one… so presumably there’s a subtle interaction there that’s going wrong for you. But I really don’t think this counts as a library bug.

Why do you make these weird and specific assumptions? No, the scrollbar is not randomly there and then not.

Again: these are two listboxes, side by side. Both have items in them that exceed the viewport. Both show their scrollbar. Nothing is changing except me moving the mouse. No magic is happening in the background hiding the scrollbar or changing the lists.

I will, in the next few days, create a standalone demo, then you can see it for yourself.

Alternatively we can do a remote-desktop session via Skype and I can show you the problem (and associated source) in action. Heck, I would even pay money for you to do it.