diff --git a/modules/juce_gui_basics/components/juce_Component.cpp b/modules/juce_gui_basics/components/juce_Component.cpp index 2fc36410ec..7136d49e8f 100644 --- a/modules/juce_gui_basics/components/juce_Component.cpp +++ b/modules/juce_gui_basics/components/juce_Component.cpp @@ -470,6 +470,24 @@ struct Component::ComponentHelpers for (auto* child : c.childComponentList) releaseAllCachedImageResources (*child); } + + //============================================================================== + static bool modalWouldBlockComponent (const Component& maybeBlocked, Component* modal) + { + return modal != nullptr + && modal != &maybeBlocked + && ! modal->isParentOf (&maybeBlocked) + && ! modal->canModalEventBeSentToComponent (&maybeBlocked); + } + + template + static void sendMouseEventToComponentsThatAreBlockedByModal (Component& modal, Function&& function) + { + for (auto& ms : Desktop::getInstance().getMouseSources()) + if (auto* c = ms.getComponentUnderMouse()) + if (modalWouldBlockComponent (*c, &modal)) + (c->*function) (ms, ms.getScreenPosition(), Time::getCurrentTime()); + } }; //============================================================================== @@ -1720,6 +1738,11 @@ void Component::enterModalState (bool shouldTakeKeyboardFocus, if (! isCurrentlyModal (false)) { + // While this component is in modal state it may block other components from receiving + // mouseExit events. To keep mouseEnter and mouseExit calls balanced on these components, + // we must manually force the mouse to "leave" blocked components. + ComponentHelpers::sendMouseEventToComponentsThatAreBlockedByModal (*this, &Component::internalMouseExit); + auto& mcm = *ModalComponentManager::getInstance(); mcm.startModal (this, deleteWhenDismissed); mcm.attachCallback (this, callback); @@ -1746,10 +1769,10 @@ void Component::exitModalState (int returnValue) mcm.endModal (this, returnValue); mcm.bringModalComponentsToFront(); - // If any of the mouse sources are over another Component when we exit the modal state then send a mouse enter event - for (auto& ms : Desktop::getInstance().getMouseSources()) - if (auto* c = ms.getComponentUnderMouse()) - c->internalMouseEnter (ms, ms.getScreenPosition(), Time::getCurrentTime()); + // While this component is in modal state it may block other components from receiving + // mouseEnter events. To keep mouseEnter and mouseExit calls balanced on these components, + // we must manually force the mouse to "enter" blocked components. + ComponentHelpers::sendMouseEventToComponentsThatAreBlockedByModal (*this, &Component::internalMouseEnter); } else { @@ -1772,10 +1795,7 @@ bool Component::isCurrentlyModal (bool onlyConsiderForemostModalComponent) const bool Component::isCurrentlyBlockedByAnotherModalComponent() const { - auto* mc = getCurrentlyModalComponent(); - - return ! (mc == nullptr || mc == this || mc->isParentOf (this) - || mc->canModalEventBeSentToComponent (this)); + return ComponentHelpers::modalWouldBlockComponent (*this, getCurrentlyModalComponent()); } int JUCE_CALLTYPE Component::getNumCurrentlyModalComponents() noexcept