mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
io/input: do not track scroll events as pointers
Scroll events arrived at pointerQueue.Push and went through pointerOf + deliverEnterLeaveEvents + deliverEvent like Move/Press/Release. The side effect: every scroll created or updated a state.pointers entry, populated p.entered with whatever handlers sat under the wheel position, and overwrote state.cursor based on hit-test at the scroll position. When the platform layer reports scroll with a different PointerID than mouse-move events for the same physical mouse — which the Windows backend does (scrollEvent omits PointerID, defaulting to 0, while pointerUpdate forwards Windows' assigned ID) — the scroll spawns a phantom state.pointers entry. Subsequent moves go to the mouse's "real" entry, so the phantom never receives Leave events, its entered set never empties, and the cleanup at the end of Push keeps it alive. pointerQueue.Frame then runs hit-test for it every frame at the user's last scroll position, threading state.cursor through it after the live pointer's resolution and clobbering it with whatever's under the scroll position. The wheel is positional but isn't a pointer. Treating it as one is the bug. Hit-test inline at the scroll position to find delivery targets, dispatch via deliverEvent (which already handles filter matching, scroll axis clamping, and area-local position), and return without creating or updating a state.pointers entry. Add a router-level test that fails without the fix: a Move sets the cursor over a CursorPointer region, a subsequent Scroll over a CursorText region, and the test asserts the cursor is still CursorPointer. Pre-fix the scroll's deliverEnterLeaveEvents overwrites state.cursor with CursorText. Signed-off-by: Eugene <eugenebosyakov@gmail.com>
This commit is contained in:
+16
-3
@@ -739,6 +739,10 @@ func (q *pointerQueue) Push(handlers map[event.Tag]*handler, state pointerState,
|
||||
state.pointers = nil
|
||||
return state, evts
|
||||
}
|
||||
if e.Kind == pointer.Scroll {
|
||||
// Scroll events are not bound to a pointer; see pointer.Event.PointerID.
|
||||
return state, q.deliverScrollEvent(handlers, evts, e)
|
||||
}
|
||||
state, pidx := state.pointerOf(e)
|
||||
p := state.pointers[pidx]
|
||||
|
||||
@@ -761,9 +765,6 @@ func (q *pointerQueue) Push(handlers map[event.Tag]*handler, state pointerState,
|
||||
p.pressed = false
|
||||
p, evts, state.cursor, _ = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, e)
|
||||
p, evts = q.deliverDropEvent(handlers, p, evts)
|
||||
case pointer.Scroll:
|
||||
p, evts, state.cursor, _ = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, e)
|
||||
evts = q.deliverEvent(handlers, p, evts, e)
|
||||
default:
|
||||
panic("unsupported pointer event type")
|
||||
}
|
||||
@@ -780,6 +781,18 @@ func (q *pointerQueue) Push(handlers map[event.Tag]*handler, state pointerState,
|
||||
return state, evts
|
||||
}
|
||||
|
||||
// deliverScrollEvent delivers scroll events to the handlers hit by the event coordinate.
|
||||
func (q *pointerQueue) deliverScrollEvent(handlers map[event.Tag]*handler, evts []taggedEvent, e pointer.Event) []taggedEvent {
|
||||
var hits []event.Tag
|
||||
q.hitTest(e.Position, func(n *hitNode) bool {
|
||||
if _, ok := handlers[n.tag]; ok {
|
||||
hits = addHandler(hits, n.tag)
|
||||
}
|
||||
return true
|
||||
})
|
||||
return q.deliverEvent(handlers, pointerInfo{handlers: hits}, evts, e)
|
||||
}
|
||||
|
||||
func (q *pointerQueue) deliverEvent(handlers map[event.Tag]*handler, p pointerInfo, evts []taggedEvent, e pointer.Event) []taggedEvent {
|
||||
if p.pressed && len(p.handlers) == 1 {
|
||||
e.Priority = pointer.Grabbed
|
||||
|
||||
Reference in New Issue
Block a user