PopupMenu missing mouseExit event

In this program the two boxes are supposed to display the same state at all times, and the state is supposed to update when the mouse exits or enters the white component.

But if you open the popup menu on the white component it never gets a mouseExit event. I thought this was fixed ages ago, see PopupMenu missing mouseExit - #8 by jimc

Also if you open the menu and then change focus to another program the two components end up in different states (see picture).

Am I doing something wrong? :slight_smile:

#pragma once

#include <JuceHeader.h>
using namespace juce;

/**
 * Calls repaint on a component when the mouse enters or leaves another component.  Used to display mouse highlight
 * effects on buttons when the cursor enters their parent search row.
 */
class MouseRepainter : public MouseListener
{
public:
    MouseRepainter (Component& c, Component& whatToRepaint) : component (c), repaintTarget (whatToRepaint)
    {
        component.addMouseListener (this, true);
    }

    ~MouseRepainter() override { component.removeMouseListener (this); }

    bool isMouseOver() const { return over; }

private:
    void mouseEnter (const MouseEvent&) override
    {
        updateStatus();
        exitVsEnterCount++;
    }

    void mouseExit (const MouseEvent&) override
    {
        updateStatus();
        exitVsEnterCount--;
        DBG ("MouseListener exitVsEnterCount: " << exitVsEnterCount);
    }

    void updateStatus()
    {
        auto wasOver = over;

        over = component.isMouseOverOrDragging (true) && ! component.isCurrentlyBlockedByAnotherModalComponent();

        if (wasOver != over)
            repaintTarget.repaint();
    }

    bool over{};
    Component& component;
    Component& repaintTarget;
    int exitVsEnterCount{};
};

class DemoComponent : public Component
{
public:
    void paint (Graphics& g) override
    {
        g.fillAll (Colours::white);
        g.setColour (Colours::black);
        g.setFont (Font (16.0f));
        g.drawText (isMouseOver() ? "Mouse over" : "Mouse not over", getLocalBounds(), Justification::centred, true);
    }

    void mouseEnter (const MouseEvent&) override { repaint(); }
    void mouseExit (const MouseEvent&) override
    {
        DBG ("white component mouseExit");
        repaint();
    }

    void mouseDown (const MouseEvent& e) override
    {
        if (e.mods.isPopupMenu())
        {
            PopupMenu m;
            m.addItem (1, "Item 1");
            m.addItem (2, "Item 2");
            m.addItem (3, "Item 3");
            m.showMenuAsync (PopupMenu::Options(), [this] (int result) { DBG ("Chose item " << result); });
            DBG ("Opened Popup Menu");
        }
    }
};

class MouseListenerComponent : public Component
{
public:
    MouseListenerComponent (Component* other) : other (other) {}

    // paint green and mouseover when
    void paint (Graphics& g) override
    {
        g.fillAll (Colours::green);
        g.setColour (Colours::black);
        g.setFont (Font (16.0f));
        g.drawText (mouseRepainter.isMouseOver() ? "Mouse over" : "Mouse not over", getLocalBounds(), Justification::centred, true);
    }

    Component* other;
    MouseRepainter mouseRepainter{ *other, *this };
};

class MainComponent : public juce::Component
{
public:
    MainComponent()
    {
        setSize (600, 400);
        addAndMakeVisible (demoComponent);
        addAndMakeVisible (mouseListenerComponent);
    }

    ~MainComponent() override = default;

    void paint (juce::Graphics& g) override { g.fillAll (Colour::greyLevel (0.05f)); }

    void resized() override
    {
        auto b = getLocalBounds().withSizeKeepingCentre (300, 150);
        demoComponent.setBounds (b.removeFromTop (50));
        mouseListenerComponent.setBounds (b.removeFromBottom (50));
    }

private:
    DemoComponent demoComponent;
    MouseListenerComponent mouseListenerComponent{ &demoComponent };

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};