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:
parent
0e20a6fdc5
commit
f2de0f12b0
1 changed files with 105 additions and 122 deletions
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue