Prevent pointer events when interacting with scroll view (#10251) 40592c7963

Currently, when items are inside a scroll view, when the scroll view is dragged and released, the item's click event will still trigger its listener if the same item is hovered. This adds a way to disable pointer events. In this implementation, when a scroll drag begins, we set the GestureClickPhase to disabled which prevents clicks from being captured.

Co-authored-by: Philip Chung <philterdesign@gmail.com>
This commit is contained in:
philter
2025-08-05 17:50:48 +00:00
parent 6fa5c20b64
commit ac160033db
10 changed files with 121 additions and 6 deletions

View File

@@ -1 +1 @@
210c1fd1762397310aae3d64b569e99d9ba0e9ea
40592c79637cab7fd3b0e466ee417080bd0671ab

View File

@@ -28,6 +28,8 @@ public:
HitResult pointerDown(Vec2D position);
HitResult pointerUp(Vec2D position);
HitResult pointerExit(Vec2D position);
HitResult dragStart(Vec2D position, float timeStamp = 0);
HitResult dragEnd(Vec2D position, float timeStamp = 0);
bool tryChangeState();
bool hitTest(Vec2D position) const;

View File

@@ -130,6 +130,10 @@ public:
HitResult pointerDown(Vec2D position) override;
HitResult pointerUp(Vec2D position) override;
HitResult pointerExit(Vec2D position) override;
HitResult dragStart(Vec2D position,
float timeStamp = 0,
bool disablePointer = true);
HitResult dragEnd(Vec2D position, float timeStamp = 0);
bool tryChangeState();
bool hitTest(Vec2D position) const;
@@ -190,6 +194,8 @@ public:
const LayerState* layerState(size_t index);
#endif
void updateDataBinds();
void enablePointerEvents();
void disablePointerEvents();
private:
std::vector<EventReport> m_reportedEvents;
@@ -239,6 +245,8 @@ public:
float timeStamp = 0) = 0;
virtual void prepareEvent(Vec2D position, ListenerType hitType) = 0;
virtual bool hitTest(Vec2D position) const = 0;
virtual void enablePointerEvents() {}
virtual void disablePointerEvents() {}
#ifdef TESTING
int earlyOutCount = 0;
#endif

View File

@@ -7,6 +7,7 @@ enum class GestureClickPhase : int
out = 0,
down = 1,
clicked = 2,
disabled = 3,
};
}
#endif

View File

@@ -12,6 +12,8 @@ enum class ListenerType : int
event = 5,
click = 6,
draggableConstraint = 7,
dragStart = 8,
dragEnd = 9,
};
}
#endif

View File

@@ -21,7 +21,6 @@ struct PointerEvent
// add more fields as needed
};
} // namespace rive
#endif

View File

@@ -84,6 +84,24 @@ HitResult NestedStateMachine::pointerExit(Vec2D position)
return HitResult::none;
}
HitResult NestedStateMachine::dragStart(Vec2D position, float timeStamp)
{
if (m_StateMachineInstance != nullptr)
{
return m_StateMachineInstance->dragStart(position, timeStamp);
}
return HitResult::none;
}
HitResult NestedStateMachine::dragEnd(Vec2D position, float timeStamp)
{
if (m_StateMachineInstance != nullptr)
{
return m_StateMachineInstance->dragEnd(position, timeStamp);
}
return HitResult::none;
}
NestedInput* NestedStateMachine::input(size_t index)
{
if (index < m_nestedInputs.size())

View File

@@ -530,14 +530,23 @@ public:
void unhover() { m_isHovered = false; }
void reset()
{
m_isConsumed = false;
m_prevIsHovered = m_isHovered;
m_isHovered = false;
if (m_clickPhase != GestureClickPhase::disabled)
{
m_isConsumed = false;
m_prevIsHovered = m_isHovered;
m_isHovered = false;
}
if (m_clickPhase == GestureClickPhase::clicked)
{
m_clickPhase = GestureClickPhase::out;
}
}
virtual void enable() { m_clickPhase = GestureClickPhase::out; }
virtual void disable()
{
m_clickPhase = GestureClickPhase::disabled;
consume();
}
bool isConsumed() { return m_isConsumed; }
bool isHovered() { return m_isHovered; }
bool prevHovered() { return m_prevIsHovered; }
@@ -688,6 +697,9 @@ public:
delete m_draggable;
}
void enable() override {}
void disable() override {}
DraggableConstraint* constraint() { return m_constraint; }
bool canEarlyOut(Component* drawable) override { return false; }
@@ -718,8 +730,9 @@ public:
m_draggable->endDrag(position, timeStamp);
if (hasScrolled)
{
return ProcessEventResult::scroll;
stateMachineInstance->dragEnd(position, timeStamp);
hasScrolled = false;
return ProcessEventResult::scroll;
}
}
else if (prevPhase != GestureClickPhase::down &&
@@ -732,6 +745,10 @@ public:
clickPhase() == GestureClickPhase::down)
{
m_draggable->drag(position, timeStamp);
if (!hasScrolled)
{
stateMachineInstance->dragStart(position, timeStamp, false);
}
hasScrolled = true;
return ProcessEventResult::scroll;
}
@@ -859,6 +876,22 @@ public:
}
listeners.push_back(listenerGroup);
}
void enablePointerEvents() override
{
for (auto listenerGroup : listeners)
{
listenerGroup->enable();
}
}
void disablePointerEvents() override
{
for (auto listenerGroup : listeners)
{
listenerGroup->disable();
}
}
};
/// Representation of a HitDrawable with a Hittable component
@@ -987,6 +1020,14 @@ public:
nestedStateMachine->pointerMove(nestedPosition,
timeStamp);
break;
case ListenerType::dragStart:
nestedStateMachine->dragStart(nestedPosition,
timeStamp);
break;
case ListenerType::dragEnd:
nestedStateMachine->dragEnd(nestedPosition,
timeStamp);
break;
case ListenerType::enter:
case ListenerType::exit:
case ListenerType::event:
@@ -1004,6 +1045,8 @@ public:
case ListenerType::move:
nestedStateMachine->pointerExit(nestedPosition);
break;
case ListenerType::dragStart:
case ListenerType::dragEnd:
case ListenerType::enter:
case ListenerType::exit:
case ListenerType::event:
@@ -1091,6 +1134,12 @@ public:
itemHitResult =
stateMachine->pointerMove(listPosition);
break;
case ListenerType::dragStart:
stateMachine->dragStart(listPosition);
break;
case ListenerType::dragEnd:
stateMachine->dragEnd(listPosition);
break;
case ListenerType::enter:
case ListenerType::exit:
case ListenerType::event:
@@ -1108,6 +1157,8 @@ public:
case ListenerType::move:
stateMachine->pointerExit(listPosition);
break;
case ListenerType::dragStart:
case ListenerType::dragEnd:
case ListenerType::enter:
case ListenerType::exit:
case ListenerType::event:
@@ -1216,6 +1267,24 @@ HitResult StateMachineInstance::pointerExit(Vec2D position)
{
return updateListeners(position, ListenerType::exit);
}
HitResult StateMachineInstance::dragStart(Vec2D position,
float timeStamp,
bool disablePointer)
{
if (disablePointer)
{
disablePointerEvents();
}
auto hit = updateListeners(position, ListenerType::dragStart);
return hit;
}
HitResult StateMachineInstance::dragEnd(Vec2D position, float timeStamp)
{
enablePointerEvents();
auto hit = updateListeners(position, ListenerType::dragEnd);
pointerMove(position, timeStamp);
return hit;
}
#ifdef TESTING
const LayerState* StateMachineInstance::layerState(size_t index)
@@ -2027,6 +2096,22 @@ void StateMachineInstance::notifyEventListeners(
}
}
void StateMachineInstance::enablePointerEvents()
{
for (const auto& hitShape : m_hitComponents)
{
hitShape->enablePointerEvents();
}
}
void StateMachineInstance::disablePointerEvents()
{
for (const auto& hitShape : m_hitComponents)
{
hitShape->disablePointerEvents();
}
}
BindableProperty* StateMachineInstance::bindablePropertyInstance(
BindableProperty* bindableProperty) const
{