1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

GUI: Ensured that mouse listeners are still called even if the originating component is deleted in response to such an event

This commit is contained in:
Fabian Renn-Giles 2022-11-02 22:52:49 +00:00
parent 0e20a6fdc5
commit f2de0f12b0

View file

@ -39,6 +39,51 @@ static Component* findFirstEnabledAncestor (Component* in)
Component* Component::currentlyFocusedComponent = nullptr;
//==============================================================================
class HierarchyChecker
{
public:
HierarchyChecker (Component* comp, const MouseEvent& originalEvent)
: me (originalEvent)
{
for (; comp != nullptr; comp = comp->getParentComponent())
hierarchy.emplace_back (comp);
}
Component* nearestNonNullParent() const
{
for (auto& comp : hierarchy)
if (comp != nullptr)
return comp;
return nullptr;
}
bool shouldBailOut() const
{
return nearestNonNullParent() == nullptr;
}
MouseEvent eventWithNearestParent() const
{
auto* comp = nearestNonNullParent();
return { me.source,
me.position.toFloat(),
me.mods,
me.pressure, me.orientation, me.rotation,
me.tiltX, me.tiltY,
comp, comp,
me.eventTime,
me.mouseDownPosition.toFloat(),
me.mouseDownTime,
me.getNumberOfClicks(),
me.mouseWasDraggedSinceMouseDown() };
}
private:
std::vector<Component::SafePointer<Component>> hierarchy;
const MouseEvent me;
};
//==============================================================================
class Component::MouseListenerList
@ -75,51 +120,14 @@ public:
}
}
// g++ 4.8 cannot deduce the parameter pack inside the function pointer when it has more than one element
#if defined(__GNUC__) && __GNUC__ < 5 && ! defined(__clang__)
template <typename... Params>
static void sendMouseEvent (Component& comp, Component::BailOutChecker& checker,
void (MouseListener::*eventMethod) (const MouseEvent&),
Params... params)
{
sendMouseEvent <decltype (eventMethod), Params...> (comp, checker, eventMethod, params...);
}
template <typename... Params>
static void sendMouseEvent (Component& comp, Component::BailOutChecker& checker,
void (MouseListener::*eventMethod) (const MouseEvent&, const MouseWheelDetails&),
Params... params)
{
sendMouseEvent <decltype (eventMethod), Params...> (comp, checker, eventMethod, params...);
}
template <typename... Params>
static void sendMouseEvent (Component& comp, Component::BailOutChecker& checker,
void (MouseListener::*eventMethod) (const MouseEvent&, float),
Params... params)
{
sendMouseEvent <decltype (eventMethod), Params...> (comp, checker, eventMethod, params...);
}
template <typename EventMethod, typename... Params>
static void sendMouseEvent (Component& comp, Component::BailOutChecker& checker,
EventMethod eventMethod,
Params... params)
#else
template <typename... Params>
static void sendMouseEvent (Component& comp, Component::BailOutChecker& checker,
void (MouseListener::*eventMethod) (Params...),
Params... params)
#endif
static void sendMouseEvent (HierarchyChecker& checker, EventMethod&& eventMethod, Params&&... params)
{
if (checker.shouldBailOut())
return;
if (auto* list = comp.mouseListeners.get())
if (auto* list = checker.nearestNonNullParent()->mouseListeners.get())
{
for (int i = list->listeners.size(); --i >= 0;)
{
(list->listeners.getUnchecked(i)->*eventMethod) (params...);
(list->listeners.getUnchecked (i)->*eventMethod) (checker.eventWithNearestParent(), params...);
if (checker.shouldBailOut())
return;
@ -128,19 +136,22 @@ public:
}
}
for (Component* p = comp.parentComponent; p != nullptr; p = p->parentComponent)
for (Component* p = checker.nearestNonNullParent()->parentComponent; p != nullptr; p = p->parentComponent)
{
if (auto* list = p->mouseListeners.get())
{
if (list->numDeepMouseListeners > 0)
{
BailOutChecker2 checker2 (checker, p);
const auto shouldBailOut = [&checker, safePointer = WeakReference { p }]
{
return checker.shouldBailOut() || safePointer == nullptr;
};
for (int i = list->numDeepMouseListeners; --i >= 0;)
{
(list->listeners.getUnchecked(i)->*eventMethod) (params...);
(list->listeners.getUnchecked(i)->*eventMethod) (checker.eventWithNearestParent(), params...);
if (checker2.shouldBailOut())
if (shouldBailOut())
return;
i = jmin (i, list->numDeepMouseListeners);
@ -154,25 +165,6 @@ private:
Array<MouseListener*> listeners;
int numDeepMouseListeners = 0;
struct BailOutChecker2
{
BailOutChecker2 (Component::BailOutChecker& boc, Component* comp)
: checker (boc), safePointer (comp)
{
}
bool shouldBailOut() const noexcept
{
return checker.shouldBailOut() || safePointer == nullptr;
}
private:
Component::BailOutChecker& checker;
const WeakReference<Component> safePointer;
JUCE_DECLARE_NON_COPYABLE (BailOutChecker2)
};
JUCE_DECLARE_NON_COPYABLE (MouseListenerList)
};
@ -2422,8 +2414,6 @@ void Component::internalMouseEnter (MouseInputSource source, Point<float> relati
if (flags.repaintOnMouseActivityFlag)
repaint();
BailOutChecker checker (this);
const auto me = makeMouseEvent (source,
PointerState().withPosition (relativePos),
source.getCurrentModifiers(),
@ -2434,6 +2424,8 @@ void Component::internalMouseEnter (MouseInputSource source, Point<float> relati
time,
0,
false);
HierarchyChecker checker (this, me);
mouseEnter (me);
flags.cachedMouseInsideComponent = true;
@ -2442,8 +2434,7 @@ void Component::internalMouseEnter (MouseInputSource source, Point<float> relati
return;
Desktop::getInstance().getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseEnter (me); });
MouseListenerList::template sendMouseEvent<const MouseEvent&> (*this, checker, &MouseListener::mouseEnter, me);
MouseListenerList::sendMouseEvent (checker, &MouseListener::mouseEnter);
}
void Component::internalMouseExit (MouseInputSource source, Point<float> relativePos, Time time)
@ -2460,8 +2451,6 @@ void Component::internalMouseExit (MouseInputSource source, Point<float> relativ
flags.cachedMouseInsideComponent = false;
BailOutChecker checker (this);
const auto me = makeMouseEvent (source,
PointerState().withPosition (relativePos),
source.getCurrentModifiers(),
@ -2473,20 +2462,32 @@ void Component::internalMouseExit (MouseInputSource source, Point<float> relativ
0,
false);
HierarchyChecker checker (this, me);
mouseExit (me);
if (checker.shouldBailOut())
return;
Desktop::getInstance().getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseExit (me); });
MouseListenerList::template sendMouseEvent<const MouseEvent&> (*this, checker, &MouseListener::mouseExit, me);
MouseListenerList::sendMouseEvent (checker, &MouseListener::mouseExit);
}
void Component::internalMouseDown (MouseInputSource source, const PointerState& relativePointerState, Time time)
{
auto& desktop = Desktop::getInstance();
BailOutChecker checker (this);
const auto me = makeMouseEvent (source,
relativePointerState,
source.getCurrentModifiers(),
this,
this,
time,
relativePointerState.position,
time,
source.getNumberOfMultipleClicks(),
false);
HierarchyChecker checker (this, me);
if (isCurrentlyBlockedByAnotherModalComponent())
{
@ -2501,18 +2502,7 @@ void Component::internalMouseDown (MouseInputSource source, const PointerState&
if (isCurrentlyBlockedByAnotherModalComponent())
{
// allow blocked mouse-events to go to global listeners..
const auto me = makeMouseEvent (source,
relativePointerState,
source.getCurrentModifiers(),
this,
this,
time,
relativePointerState.position,
time,
source.getNumberOfMultipleClicks(),
false);
desktop.getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseDown (me); });
desktop.getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseDown (checker.eventWithNearestParent()); });
return;
}
}
@ -2541,24 +2531,14 @@ void Component::internalMouseDown (MouseInputSource source, const PointerState&
if (flags.repaintOnMouseActivityFlag)
repaint();
const auto me = makeMouseEvent (source,
relativePointerState,
source.getCurrentModifiers(),
this,
this,
time,
relativePointerState.position,
time,
source.getNumberOfMultipleClicks(),
false);
mouseDown (me);
if (checker.shouldBailOut())
return;
desktop.getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseDown (me); });
desktop.getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseDown (checker.eventWithNearestParent()); });
MouseListenerList::template sendMouseEvent<const MouseEvent&> (*this, checker, &MouseListener::mouseDown, me);
MouseListenerList::sendMouseEvent (checker, &MouseListener::mouseDown);
}
void Component::internalMouseUp (MouseInputSource source, const PointerState& relativePointerState, Time time, const ModifierKeys oldModifiers)
@ -2566,11 +2546,6 @@ void Component::internalMouseUp (MouseInputSource source, const PointerState& re
if (flags.mouseDownWasBlocked && isCurrentlyBlockedByAnotherModalComponent())
return;
BailOutChecker checker (this);
if (flags.repaintOnMouseActivityFlag)
repaint();
const auto me = makeMouseEvent (source,
relativePointerState,
oldModifiers,
@ -2581,15 +2556,21 @@ void Component::internalMouseUp (MouseInputSource source, const PointerState& re
source.getLastMouseDownTime(),
source.getNumberOfMultipleClicks(),
source.isLongPressOrDrag());
HierarchyChecker checker (this, me);
if (flags.repaintOnMouseActivityFlag)
repaint();
mouseUp (me);
if (checker.shouldBailOut())
return;
auto& desktop = Desktop::getInstance();
desktop.getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseUp (me); });
desktop.getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseUp (checker.eventWithNearestParent()); });
MouseListenerList::template sendMouseEvent<const MouseEvent&> (*this, checker, &MouseListener::mouseUp, me);
MouseListenerList::sendMouseEvent (checker, &MouseListener::mouseUp);
if (checker.shouldBailOut())
return;
@ -2597,13 +2578,13 @@ void Component::internalMouseUp (MouseInputSource source, const PointerState& re
// check for double-click
if (me.getNumberOfClicks() >= 2)
{
mouseDoubleClick (me);
mouseDoubleClick (checker.eventWithNearestParent());
if (checker.shouldBailOut())
return;
desktop.mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseDoubleClick (me); });
MouseListenerList::template sendMouseEvent<const MouseEvent&> (*this, checker, &MouseListener::mouseDoubleClick, me);
desktop.mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseDoubleClick (checker.eventWithNearestParent()); });
MouseListenerList::sendMouseEvent (checker, &MouseListener::mouseDoubleClick);
}
}
@ -2611,8 +2592,6 @@ void Component::internalMouseDrag (MouseInputSource source, const PointerState&
{
if (! isCurrentlyBlockedByAnotherModalComponent())
{
BailOutChecker checker (this);
const auto me = makeMouseEvent (source,
relativePointerState,
source.getCurrentModifiers(),
@ -2623,14 +2602,16 @@ void Component::internalMouseDrag (MouseInputSource source, const PointerState&
source.getLastMouseDownTime(),
source.getNumberOfMultipleClicks(),
source.isLongPressOrDrag());
HierarchyChecker checker (this, me);
mouseDrag (me);
if (checker.shouldBailOut())
return;
Desktop::getInstance().getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseDrag (me); });
MouseListenerList::template sendMouseEvent<const MouseEvent&> (*this, checker, &MouseListener::mouseDrag, me);
Desktop::getInstance().getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseDrag (checker.eventWithNearestParent()); });
MouseListenerList::sendMouseEvent (checker, &MouseListener::mouseDrag);
}
}
@ -2645,8 +2626,6 @@ void Component::internalMouseMove (MouseInputSource source, Point<float> relativ
}
else
{
BailOutChecker checker (this);
const auto me = makeMouseEvent (source,
PointerState().withPosition (relativePos),
source.getCurrentModifiers(),
@ -2657,14 +2636,16 @@ void Component::internalMouseMove (MouseInputSource source, Point<float> relativ
time,
0,
false);
HierarchyChecker checker (this, me);
mouseMove (me);
if (checker.shouldBailOut())
return;
desktop.getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseMove (me); });
MouseListenerList::template sendMouseEvent<const MouseEvent&> (*this, checker, &MouseListener::mouseMove, me);
desktop.getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseMove (checker.eventWithNearestParent()); });
MouseListenerList::sendMouseEvent (checker, &MouseListener::mouseMove);
}
}
@ -2672,7 +2653,6 @@ void Component::internalMouseWheel (MouseInputSource source, Point<float> relati
Time time, const MouseWheelDetails& wheel)
{
auto& desktop = Desktop::getInstance();
BailOutChecker checker (this);
const auto me = makeMouseEvent (source,
PointerState().withPosition (relativePos),
@ -2685,6 +2665,8 @@ void Component::internalMouseWheel (MouseInputSource source, Point<float> relati
0,
false);
HierarchyChecker checker (this, me);
if (isCurrentlyBlockedByAnotherModalComponent())
{
// allow blocked mouse-events to go to global listeners..
@ -2697,10 +2679,10 @@ void Component::internalMouseWheel (MouseInputSource source, Point<float> relati
if (checker.shouldBailOut())
return;
desktop.mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseWheelMove (me, wheel); });
desktop.mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseWheelMove (checker.eventWithNearestParent(), wheel); });
if (! checker.shouldBailOut())
MouseListenerList::template sendMouseEvent<const MouseEvent&, const MouseWheelDetails&> (*this, checker, &MouseListener::mouseWheelMove, me, wheel);
MouseListenerList::sendMouseEvent (checker, &MouseListener::mouseWheelMove, wheel);
}
}
@ -2708,7 +2690,6 @@ void Component::internalMagnifyGesture (MouseInputSource source, Point<float> re
Time time, float amount)
{
auto& desktop = Desktop::getInstance();
BailOutChecker checker (this);
const auto me = makeMouseEvent (source,
PointerState().withPosition (relativePos),
@ -2721,6 +2702,8 @@ void Component::internalMagnifyGesture (MouseInputSource source, Point<float> re
0,
false);
HierarchyChecker checker (this, me);
if (isCurrentlyBlockedByAnotherModalComponent())
{
// allow blocked mouse-events to go to global listeners..
@ -2733,10 +2716,10 @@ void Component::internalMagnifyGesture (MouseInputSource source, Point<float> re
if (checker.shouldBailOut())
return;
desktop.mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseMagnify (me, amount); });
desktop.mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseMagnify (checker.eventWithNearestParent(), amount); });
if (! checker.shouldBailOut())
MouseListenerList::template sendMouseEvent<const MouseEvent&, float> (*this, checker, &MouseListener::mouseMagnify, me, amount);
MouseListenerList::sendMouseEvent (checker, &MouseListener::mouseMagnify, amount);
}
}