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

Component: Add more stringent checks for deleted components when handling mouse events

This commit is contained in:
reuk 2025-02-05 12:40:45 +00:00
parent 86d4835e2f
commit 95610e6c5e
No known key found for this signature in database
4 changed files with 174 additions and 139 deletions

View file

@ -57,14 +57,57 @@ Component* Component::currentlyFocusedComponent = nullptr;
class HierarchyChecker
{
public:
HierarchyChecker (Component* comp, const MouseEvent& originalEvent)
: me (originalEvent)
/* Creates a bail-out checker for comp and its ancestors, that will return true from
shouldBailOut() if all of comp's ancestors are destroyed.
@param comp a safe pointer to a component. The pointer will be updated to point
to the nearest non-null ancestor on each call to shouldBailOut.
*/
HierarchyChecker (Component::SafePointer<Component>* comp, const MouseEvent& originalEvent)
: closestAncestor (*comp),
me (originalEvent)
{
for (; comp != nullptr; comp = comp->getParentComponent())
hierarchy.emplace_back (comp);
for (Component* c = *comp; c != nullptr; c = c->getParentComponent())
hierarchy.emplace_back (c);
}
Component* nearestNonNullParent() const
{
return closestAncestor;
}
/* Searches for the closest ancestor, and returns true if the closest ancestor is nullptr. */
bool shouldBailOut() const
{
closestAncestor = findNearestNonNullParent();
return closestAncestor == nullptr;
}
MouseEvent eventWithNearestParent() const
{
return { me.source,
me.position.toFloat(),
me.mods,
me.pressure, me.orientation, me.rotation,
me.tiltX, me.tiltY,
closestAncestor,
closestAncestor,
me.eventTime,
me.mouseDownPosition.toFloat(),
me.mouseDownTime,
me.getNumberOfClicks(),
me.mouseWasDraggedSinceMouseDown() };
}
template <typename Callback>
void forEach (Callback&& callback)
{
for (auto& item : hierarchy)
if (item != nullptr)
callback (*item);
}
private:
Component* findNearestNonNullParent() const
{
for (auto& comp : hierarchy)
if (comp != nullptr)
@ -73,28 +116,7 @@ public:
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:
Component::SafePointer<Component>& closestAncestor;
std::vector<Component::SafePointer<Component>> hierarchy;
const MouseEvent me;
};
@ -2083,33 +2105,36 @@ void Component::removeMouseListener (MouseListener* listenerToRemove)
}
//==============================================================================
void Component::internalMouseEnter (MouseInputSource source, Point<float> relativePos, Time time)
void Component::internalMouseEnter (SafePointer<Component> target, MouseInputSource source, Point<float> relativePos, Time time)
{
if (isCurrentlyBlockedByAnotherModalComponent())
if (target->isCurrentlyBlockedByAnotherModalComponent())
{
// if something else is modal, always just show a normal mouse cursor
source.showMouseCursor (MouseCursor::NormalCursor);
return;
}
if (flags.repaintOnMouseActivityFlag)
repaint();
if (target->flags.repaintOnMouseActivityFlag)
target->repaint();
const auto me = makeMouseEvent (source,
detail::PointerState().withPosition (relativePos),
source.getCurrentModifiers(),
this,
this,
target,
target,
time,
relativePos,
time,
0,
false);
HierarchyChecker checker (this, me);
mouseEnter (me);
HierarchyChecker checker (&target, me);
target->mouseEnter (me);
flags.cachedMouseInsideComponent = true;
if (checker.shouldBailOut())
return;
target->flags.cachedMouseInsideComponent = true;
if (checker.shouldBailOut())
return;
@ -2118,33 +2143,33 @@ void Component::internalMouseEnter (MouseInputSource source, Point<float> relati
MouseListenerList::sendMouseEvent (checker, &MouseListener::mouseEnter);
}
void Component::internalMouseExit (MouseInputSource source, Point<float> relativePos, Time time)
void Component::internalMouseExit (SafePointer<Component> target, MouseInputSource source, Point<float> relativePos, Time time)
{
if (isCurrentlyBlockedByAnotherModalComponent())
if (target->isCurrentlyBlockedByAnotherModalComponent())
{
// if something else is modal, always just show a normal mouse cursor
source.showMouseCursor (MouseCursor::NormalCursor);
return;
}
if (flags.repaintOnMouseActivityFlag)
repaint();
if (target->flags.repaintOnMouseActivityFlag)
target->repaint();
flags.cachedMouseInsideComponent = false;
target->flags.cachedMouseInsideComponent = false;
const auto me = makeMouseEvent (source,
detail::PointerState().withPosition (relativePos),
source.getCurrentModifiers(),
this,
this,
target,
target,
time,
relativePos,
time,
0,
false);
HierarchyChecker checker (this, me);
mouseExit (me);
HierarchyChecker checker (&target, me);
target->mouseExit (me);
if (checker.shouldBailOut())
return;
@ -2153,7 +2178,8 @@ void Component::internalMouseExit (MouseInputSource source, Point<float> relativ
MouseListenerList::sendMouseEvent (checker, &MouseListener::mouseExit);
}
void Component::internalMouseDown (MouseInputSource source,
void Component::internalMouseDown (SafePointer<Component> target,
MouseInputSource source,
const detail::PointerState& relativePointerState,
Time time)
{
@ -2162,27 +2188,27 @@ void Component::internalMouseDown (MouseInputSource source,
const auto me = makeMouseEvent (source,
relativePointerState,
source.getCurrentModifiers(),
this,
this,
target,
target,
time,
relativePointerState.position,
time,
source.getNumberOfMultipleClicks(),
false);
HierarchyChecker checker (this, me);
HierarchyChecker checker (&target, me);
if (isCurrentlyBlockedByAnotherModalComponent())
if (target->isCurrentlyBlockedByAnotherModalComponent())
{
flags.mouseDownWasBlocked = true;
internalModalInputAttempt();
target->flags.mouseDownWasBlocked = true;
target->internalModalInputAttempt();
if (checker.shouldBailOut())
return;
// If processing the input attempt has exited the modal loop, we'll allow the event
// to be delivered..
if (isCurrentlyBlockedByAnotherModalComponent())
if (target->isCurrentlyBlockedByAnotherModalComponent())
{
// allow blocked mouse-events to go to global listeners..
desktop.getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseDown (checker.eventWithNearestParent()); });
@ -2190,28 +2216,26 @@ void Component::internalMouseDown (MouseInputSource source,
}
}
flags.mouseDownWasBlocked = false;
target->flags.mouseDownWasBlocked = false;
for (auto* c = this; c != nullptr; c = c->parentComponent)
checker.forEach ([] (auto& comp)
{
if (c->isBroughtToFrontOnMouseClick())
{
c->toFront (true);
if (checker.shouldBailOut())
return;
}
}
grabKeyboardFocusInternal (focusChangedByMouseClick, true, FocusChangeDirection::unknown);
if (comp.isBroughtToFrontOnMouseClick())
comp.toFront (true);
});
if (checker.shouldBailOut())
return;
if (flags.repaintOnMouseActivityFlag)
repaint();
target->grabKeyboardFocusInternal (focusChangedByMouseClick, true, FocusChangeDirection::unknown);
mouseDown (me);
if (checker.shouldBailOut())
return;
if (target->flags.repaintOnMouseActivityFlag)
target->repaint();
target->mouseDown (me);
if (checker.shouldBailOut())
return;
@ -2221,25 +2245,28 @@ void Component::internalMouseDown (MouseInputSource source,
MouseListenerList::sendMouseEvent (checker, &MouseListener::mouseDown);
}
void Component::internalMouseUp (MouseInputSource source,
void Component::internalMouseUp (SafePointer<Component> target,
MouseInputSource source,
const detail::PointerState& relativePointerState,
Time time,
const ModifierKeys oldModifiers)
{
const auto originalTarget = target;
const auto me = makeMouseEvent (source,
relativePointerState,
oldModifiers,
this,
this,
target,
target,
time,
getLocalPoint (nullptr, source.getLastMouseDownPosition()),
target->getLocalPoint (nullptr, source.getLastMouseDownPosition()),
source.getLastMouseDownTime(),
source.getNumberOfMultipleClicks(),
source.isLongPressOrDrag());
HierarchyChecker checker (this, me);
HierarchyChecker checker (&target, me);
if (flags.mouseDownWasBlocked && isCurrentlyBlockedByAnotherModalComponent())
if (target->flags.mouseDownWasBlocked && target->isCurrentlyBlockedByAnotherModalComponent())
{
// Global listeners still need to know about the mouse up
auto& desktop = Desktop::getInstance();
@ -2247,10 +2274,10 @@ void Component::internalMouseUp (MouseInputSource source,
return;
}
if (flags.repaintOnMouseActivityFlag)
repaint();
if (target->flags.repaintOnMouseActivityFlag)
target->repaint();
mouseUp (me);
target->mouseUp (me);
if (checker.shouldBailOut())
return;
@ -2266,8 +2293,8 @@ void Component::internalMouseUp (MouseInputSource source,
// check for double-click
if (me.getNumberOfClicks() >= 2)
{
if (checker.nearestNonNullParent() == this)
mouseDoubleClick (checker.eventWithNearestParent());
if (target == originalTarget)
target->mouseDoubleClick (checker.eventWithNearestParent());
if (checker.shouldBailOut())
return;
@ -2277,24 +2304,24 @@ void Component::internalMouseUp (MouseInputSource source,
}
}
void Component::internalMouseDrag (MouseInputSource source, const detail::PointerState& relativePointerState, Time time)
void Component::internalMouseDrag (SafePointer<Component> target, MouseInputSource source, const detail::PointerState& relativePointerState, Time time)
{
if (! isCurrentlyBlockedByAnotherModalComponent())
if (! target->isCurrentlyBlockedByAnotherModalComponent())
{
const auto me = makeMouseEvent (source,
relativePointerState,
source.getCurrentModifiers(),
this,
this,
target,
target,
time,
getLocalPoint (nullptr, source.getLastMouseDownPosition()),
target->getLocalPoint (nullptr, source.getLastMouseDownPosition()),
source.getLastMouseDownTime(),
source.getNumberOfMultipleClicks(),
source.isLongPressOrDrag());
HierarchyChecker checker (this, me);
HierarchyChecker checker (&target, me);
mouseDrag (me);
target->mouseDrag (me);
if (checker.shouldBailOut())
return;
@ -2304,11 +2331,11 @@ void Component::internalMouseDrag (MouseInputSource source, const detail::Pointe
}
}
void Component::internalMouseMove (MouseInputSource source, Point<float> relativePos, Time time)
void Component::internalMouseMove (SafePointer<Component> target, MouseInputSource source, Point<float> relativePos, Time time)
{
auto& desktop = Desktop::getInstance();
if (isCurrentlyBlockedByAnotherModalComponent())
if (target->isCurrentlyBlockedByAnotherModalComponent())
{
// allow blocked mouse-events to go to global listeners..
desktop.sendMouseMove();
@ -2318,17 +2345,17 @@ void Component::internalMouseMove (MouseInputSource source, Point<float> relativ
const auto me = makeMouseEvent (source,
detail::PointerState().withPosition (relativePos),
source.getCurrentModifiers(),
this,
this,
target,
target,
time,
relativePos,
time,
0,
false);
HierarchyChecker checker (this, me);
HierarchyChecker checker (&target, me);
mouseMove (me);
target->mouseMove (me);
if (checker.shouldBailOut())
return;
@ -2338,7 +2365,7 @@ void Component::internalMouseMove (MouseInputSource source, Point<float> relativ
}
}
void Component::internalMouseWheel (MouseInputSource source, Point<float> relativePos,
void Component::internalMouseWheel (SafePointer<Component> target, MouseInputSource source, Point<float> relativePos,
Time time, const MouseWheelDetails& wheel)
{
auto& desktop = Desktop::getInstance();
@ -2346,24 +2373,24 @@ void Component::internalMouseWheel (MouseInputSource source, Point<float> relati
const auto me = makeMouseEvent (source,
detail::PointerState().withPosition (relativePos),
source.getCurrentModifiers(),
this,
this,
target,
target,
time,
relativePos,
time,
0,
false);
HierarchyChecker checker (this, me);
HierarchyChecker checker (&target, me);
if (isCurrentlyBlockedByAnotherModalComponent())
if (target->isCurrentlyBlockedByAnotherModalComponent())
{
// allow blocked mouse-events to go to global listeners..
desktop.mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseWheelMove (me, wheel); });
}
else
{
mouseWheelMove (me, wheel);
target->mouseWheelMove (me, wheel);
if (checker.shouldBailOut())
return;
@ -2375,7 +2402,7 @@ void Component::internalMouseWheel (MouseInputSource source, Point<float> relati
}
}
void Component::internalMagnifyGesture (MouseInputSource source, Point<float> relativePos,
void Component::internalMagnifyGesture (SafePointer<Component> target, MouseInputSource source, Point<float> relativePos,
Time time, float amount)
{
auto& desktop = Desktop::getInstance();
@ -2383,24 +2410,24 @@ void Component::internalMagnifyGesture (MouseInputSource source, Point<float> re
const auto me = makeMouseEvent (source,
detail::PointerState().withPosition (relativePos),
source.getCurrentModifiers(),
this,
this,
target,
target,
time,
relativePos,
time,
0,
false);
HierarchyChecker checker (this, me);
HierarchyChecker checker (&target, me);
if (isCurrentlyBlockedByAnotherModalComponent())
if (target->isCurrentlyBlockedByAnotherModalComponent())
{
// allow blocked mouse-events to go to global listeners..
desktop.mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseMagnify (me, amount); });
}
else
{
mouseMagnify (me, amount);
target->mouseMagnify (me, amount);
if (checker.shouldBailOut())
return;

View file

@ -2692,14 +2692,14 @@ private:
uint8 componentTransparency = 0;
//==============================================================================
void internalMouseEnter (MouseInputSource, Point<float>, Time);
void internalMouseExit (MouseInputSource, Point<float>, Time);
void internalMouseDown (MouseInputSource, const detail::PointerState&, Time);
void internalMouseUp (MouseInputSource, const detail::PointerState&, Time, ModifierKeys oldModifiers);
void internalMouseDrag (MouseInputSource, const detail::PointerState&, Time);
void internalMouseMove (MouseInputSource, Point<float>, Time);
void internalMouseWheel (MouseInputSource, Point<float>, Time, const MouseWheelDetails&);
void internalMagnifyGesture (MouseInputSource, Point<float>, Time, float);
static void internalMouseEnter (SafePointer<Component>, MouseInputSource, Point<float>, Time);
static void internalMouseExit (SafePointer<Component>, MouseInputSource, Point<float>, Time);
static void internalMouseDown (SafePointer<Component>, MouseInputSource, const detail::PointerState&, Time);
static void internalMouseUp (SafePointer<Component>, MouseInputSource, const detail::PointerState&, Time, ModifierKeys oldModifiers);
static void internalMouseDrag (SafePointer<Component>, MouseInputSource, const detail::PointerState&, Time);
static void internalMouseMove (SafePointer<Component>, MouseInputSource, Point<float>, Time);
static void internalMouseWheel (SafePointer<Component>, MouseInputSource, Point<float>, Time, const MouseWheelDetails&);
static void internalMagnifyGesture (SafePointer<Component>, MouseInputSource, Point<float>, Time, float);
void internalBroughtToFront();
void internalKeyboardFocusGain (FocusChangeType, const WeakReference<Component>&, FocusChangeDirection);
void internalKeyboardFocusGain (FocusChangeType);

View file

@ -259,7 +259,7 @@ struct ComponentHelpers
for (auto& ms : Desktop::getInstance().getMouseSources())
if (auto* c = ms.getComponentUnderMouse())
if (modalWouldBlockComponent (*c, &modal))
(c->*function) (ms, SH::screenPosToLocalPos (*c, ms.getScreenPosition()), Time::getCurrentTime());
function (c, ms, SH::screenPosToLocalPos (*c, ms.getScreenPosition()), Time::getCurrentTime());
}
class ModalComponentManagerChangeNotifier

View file

@ -117,68 +117,76 @@ public:
void sendMouseEnter (Component& comp, const detail::PointerState& pointerState, Time time)
{
JUCE_MOUSE_EVENT_DBG ("enter", pointerState.position)
comp.internalMouseEnter (MouseInputSource (this),
SH::screenPosToLocalPos (comp, pointerState.position),
time);
Component::internalMouseEnter (&comp,
MouseInputSource (this),
SH::screenPosToLocalPos (comp, pointerState.position),
time);
}
void sendMouseExit (Component& comp, const detail::PointerState& pointerState, Time time)
{
JUCE_MOUSE_EVENT_DBG ("exit", pointerState.position)
comp.internalMouseExit (MouseInputSource (this),
SH::screenPosToLocalPos (comp, pointerState.position),
time);
Component::internalMouseExit (&comp,
MouseInputSource (this),
SH::screenPosToLocalPos (comp, pointerState.position),
time);
}
void sendMouseMove (Component& comp, const detail::PointerState& pointerState, Time time)
{
JUCE_MOUSE_EVENT_DBG ("move", pointerState.position)
comp.internalMouseMove (MouseInputSource (this),
SH::screenPosToLocalPos (comp, pointerState.position),
time);
Component::internalMouseMove (&comp,
MouseInputSource (this),
SH::screenPosToLocalPos (comp, pointerState.position),
time);
}
void sendMouseDown (Component& comp, const detail::PointerState& pointerState, Time time)
{
JUCE_MOUSE_EVENT_DBG ("down", pointerState.position)
comp.internalMouseDown (MouseInputSource (this),
pointerState.withPosition (SH::screenPosToLocalPos (comp, pointerState.position)),
time);
Component::internalMouseDown (&comp,
MouseInputSource (this),
pointerState.withPosition (SH::screenPosToLocalPos (comp, pointerState.position)),
time);
}
void sendMouseDrag (Component& comp, const detail::PointerState& pointerState, Time time)
{
JUCE_MOUSE_EVENT_DBG ("drag", pointerState.position)
comp.internalMouseDrag (MouseInputSource (this),
pointerState.withPosition (SH::screenPosToLocalPos (comp, pointerState.position)),
time);
Component::internalMouseDrag (&comp,
MouseInputSource (this),
pointerState.withPosition (SH::screenPosToLocalPos (comp, pointerState.position)),
time);
}
void sendMouseUp (Component& comp, const detail::PointerState& pointerState, Time time, ModifierKeys oldMods)
{
JUCE_MOUSE_EVENT_DBG ("up", pointerState.position)
comp.internalMouseUp (MouseInputSource (this),
pointerState.withPosition (SH::screenPosToLocalPos (comp, pointerState.position)),
time,
oldMods);
Component::internalMouseUp (&comp,
MouseInputSource (this),
pointerState.withPosition (SH::screenPosToLocalPos (comp, pointerState.position)),
time,
oldMods);
}
void sendMouseWheel (Component& comp, Point<float> screenPos, Time time, const MouseWheelDetails& wheel)
{
JUCE_MOUSE_EVENT_DBG ("wheel", screenPos)
comp.internalMouseWheel (MouseInputSource (this),
SH::screenPosToLocalPos (comp, screenPos),
time,
wheel);
Component::internalMouseWheel (&comp,
MouseInputSource (this),
SH::screenPosToLocalPos (comp, screenPos),
time,
wheel);
}
void sendMagnifyGesture (Component& comp, Point<float> screenPos, Time time, float amount)
{
JUCE_MOUSE_EVENT_DBG ("magnify", screenPos)
comp.internalMagnifyGesture (MouseInputSource (this),
SH::screenPosToLocalPos (comp, screenPos),
time,
amount);
Component::internalMagnifyGesture (&comp,
MouseInputSource (this),
SH::screenPosToLocalPos (comp, screenPos),
time,
amount);
}
#undef JUCE_MOUSE_EVENT_DBG