[Bug] MenubarComponent overlaps highlighted menus due to loss of precision in getLocalPoint<int>

I have created a test project based on the MenusDemo which demonstrates a problem with the highlight color sticking.

This is on a Mac, using a GUI App. Juce 7.0.8. Project:
MenusDemoOverlap.zip (19.6 KB)

  • MenubarComponent is a child of the MainComponent
  • MainComponent is scaled using an AffineTransform, i.e. 150%
  • MenubarComponent “over” color set to green here

When the menu is descended (open), the ModalComponentManager is in use. Moving from “Menu Position” to “Outer Colour” causes a MenuBarComponent::handleCommandMessage() call to be received, at which time, the following code is called:

    updateItemUnderMouse (getMouseXYRelative());

As demonstrated here, the x coordinate can be off by 1 pixel due to the rounding/loss of precision in the getMouseXYRelative call, which is based on <int> getLocalPoint, and the menu “sticks” (both menus are highlighted at the same time):

I have inserted debugging in the MenuBarComponent:

//==============================================================================
void MenuBarComponent::mouseEnter (const MouseEvent& e)
{
    DBG(String::formatted("mouseEnter: pos x %d y %d", e.getPosition().getX(), e.getPosition().getY()));
    
	if (e.eventComponent == this)
        updateItemUnderMouse (e.getPosition());
}

void MenuBarComponent::mouseExit (const MouseEvent& e)
{
    DBG(String::formatted("mouseExit : pos x %d y %d", e.getPosition().getX(), e.getPosition().getY()));

    if (e.eventComponent == this)
        updateItemUnderMouse (e.getPosition());
}

void MenuBarComponent::updateItemUnderMouse (Point<int> p)
{
    DBG(String::formatted("    updateItemUnderMouse: x %d y %d", p.getX(), p.getY()));

    setItemUnderMouse (getItemAt (p));
}

void MenuBarComponent::setItemUnderMouse (int index)
{
    if (itemUnderMouse == index)
        return;

    DBG(String::formatted("        setItemUnderMouse: index %d", index));
 
    repaintMenuItem (itemUnderMouse);
    itemUnderMouse = index;
    repaintMenuItem (itemUnderMouse);

    if (isPositiveAndBelow (itemUnderMouse, (int) itemComponents.size()))
        if (auto* handler = itemComponents[(size_t) itemUnderMouse]->getAccessibilityHandler())
            handler->grabFocus();
}

…and the following code in handleCommandMessage() to display the “current” calculation, and the “fixed” calculation which uses the <float> getLocalPoint():

    auto xyRelInt = getMouseXYRelative();   // the original way
    
    auto xyFloat = Desktop::getInstance().getMainMouseSource().getScreenPosition();
    auto xyLocFloat = getLocalPoint (nullptr, xyFloat);
    auto xyLocInt = xyLocFloat.roundToInt();
    
    DBG(String::formatted("handleCommandMessage: xyRelInt x %d y %d (current), xyLocFloat x %f y %f, xyLocInt x %d y %d (fixed)",
                          xyRelInt.getX(), xyRelInt.getY(), xyLocFloat.getX(), xyLocFloat.getY(), xyLocInt.getX(), xyLocInt.getY()));

To reproduce:

  • Set comboBox to “150%”.
  • Click DOWN and release on “Menu Position”, so menu drops down and is exposed.
    Move mouse very slowly to the right and cross into the next menu. It should stick. Or it may not, based on the app’s screen position, but it may then stick when you cross back into the first menu. Since this is all related to screen position if you place the app in different locations on the screen, it will act differently. You may have to move it around.

Here is a portion of the DBG results printed when I get it to stick:

Mouse leaves “Menu Position” menu and enters “Outer Colour” menu:

mouseEnter: pos x 122 y 20
    updateItemUnderMouse: x 122 y 20
        setItemUnderMouse: index 1	<— "Outer Colour" menu is highlighted

A mouseExit message is sent by ModalComponentManager, and handleCommandMessage is called, which calculates an x coordinate that is off by one (one less than the actual mouse position), and then turns index 0 (the first menu) back on, so both are highlighted:

mouseExit : pos x 122 y 20
    updateItemUnderMouse: x 122 y 20
handleCommandMessage: xyRelInt x 121 y 20 (current), xyLocFloat x 121.632812 y 19.919271, xyLocInt x 122 y 20 (fixed)
    updateItemUnderMouse: x 121 y 20.  <— position is actually x 122, but rounding error sends 121
        setItemUnderMouse: index 0 <— "Menu Position" menu is highlighted again (both on)

If you replace the code in handleCommandMessage() with this, it works correctly:

    // the fix:
    auto xyFloat = Desktop::getInstance().getMainMouseSource().getScreenPosition();
    auto xyLocFloat = getLocalPoint (nullptr, xyFloat);
    auto xyLocInt = xyLocFloat.roundToInt();
    updateItemUnderMouse (xyLocInt);

Thanks for sharing the demo, I can indeed reproduce the issue I’ll take a quick look.

1 Like

And what did the quick look reveal? :smile:

Oops sorry I didn’t get back to this one. I think a fix did make it in. Fairly sure this was it…

Thank you!