mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-06 01:45:36 +00:00
io/router: support multiple pointer areas for same tag
Fixes gio#129 Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+56
-41
@@ -20,6 +20,8 @@ type pointerQueue struct {
|
|||||||
handlers map[event.Tag]*pointerHandler
|
handlers map[event.Tag]*pointerHandler
|
||||||
pointers []pointerInfo
|
pointers []pointerInfo
|
||||||
reader ops.Reader
|
reader ops.Reader
|
||||||
|
|
||||||
|
scratch []event.Tag
|
||||||
}
|
}
|
||||||
|
|
||||||
type hitNode struct {
|
type hitNode struct {
|
||||||
@@ -240,18 +242,29 @@ func (q *pointerQueue) Push(e pointer.Event, events *handlerEvents) {
|
|||||||
e.Type = pointer.Drag
|
e.Type = pointer.Drag
|
||||||
}
|
}
|
||||||
|
|
||||||
q.deliverEnterLeaveEvents(p, events, e)
|
|
||||||
if e.Type == pointer.Release {
|
if e.Type == pointer.Release {
|
||||||
q.deliverEvent(p, events, e)
|
|
||||||
p.pressed = false
|
p.pressed = false
|
||||||
}
|
}
|
||||||
if !p.pressed {
|
q.scratch = q.scratch[:0]
|
||||||
if e.Type == pointer.Press {
|
q.opHit(&q.scratch, e.Position)
|
||||||
p.pressed = true
|
if p.pressed {
|
||||||
|
// Filter out non-participating handlers.
|
||||||
|
for i := len(q.scratch) - 1; i >= 0; i-- {
|
||||||
|
if _, found := searchTag(p.handlers, q.scratch[i]); !found {
|
||||||
|
q.scratch = append(q.scratch[:i], q.scratch[i+1:]...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
p.handlers = p.handlers[:0]
|
}
|
||||||
q.opHit(&p.handlers, e.Position)
|
q.deliverEnterLeaveEvents(p, q.scratch, events, e)
|
||||||
q.deliverEnterLeaveEvents(p, events, e)
|
|
||||||
|
if e.Type == pointer.Release {
|
||||||
|
q.deliverEvent(p, events, e)
|
||||||
|
}
|
||||||
|
if !p.pressed {
|
||||||
|
p.handlers = append(p.handlers[:0], q.scratch...)
|
||||||
|
}
|
||||||
|
if e.Type == pointer.Press {
|
||||||
|
p.pressed = true
|
||||||
}
|
}
|
||||||
if e.Type != pointer.Release {
|
if e.Type != pointer.Release {
|
||||||
q.deliverEvent(p, events, e)
|
q.deliverEvent(p, events, e)
|
||||||
@@ -282,45 +295,47 @@ func (q *pointerQueue) deliverEvent(p *pointerInfo, events *handlerEvents, e poi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *pointerQueue) deliverEnterLeaveEvents(p *pointerInfo, events *handlerEvents, e pointer.Event) {
|
func (q *pointerQueue) deliverEnterLeaveEvents(p *pointerInfo, hits []event.Tag, events *handlerEvents, e pointer.Event) {
|
||||||
foremost := true
|
if e.Source != pointer.Mouse && !p.pressed {
|
||||||
for _, k := range p.handlers {
|
// Consider non-mouse pointers leaving when they're released.
|
||||||
h := q.handlers[k]
|
hits = nil
|
||||||
e := e
|
}
|
||||||
if p.pressed && len(p.handlers) == 1 {
|
// Deliver Leave events.
|
||||||
e.Priority = pointer.Grabbed
|
for _, k := range p.entered {
|
||||||
} else if foremost {
|
if _, found := searchTag(hits, k); found {
|
||||||
e.Priority = pointer.Foremost
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hit-test to deliver Enter/Leave events. Consider non-mouse
|
|
||||||
// events leaving when they're Released.
|
|
||||||
hit := (e.Source == pointer.Mouse || p.pressed) && q.hit(h.area, e.Position)
|
|
||||||
entered := -1
|
|
||||||
for i, k2 := range p.entered {
|
|
||||||
if k2 == k {
|
|
||||||
entered = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Position = q.invTransform(h.area, e.Position)
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case !hit && entered != -1:
|
|
||||||
p.entered = append(p.entered[:entered], p.entered[entered+1:]...)
|
|
||||||
e.Type = pointer.Leave
|
|
||||||
case hit && entered == -1:
|
|
||||||
p.entered = append(p.entered, k)
|
|
||||||
e.Type = pointer.Enter
|
|
||||||
default:
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
h := q.handlers[k]
|
||||||
|
e.Type = pointer.Leave
|
||||||
|
e.Position = q.invTransform(h.area, e.Position)
|
||||||
|
|
||||||
if e.Type&h.types == e.Type {
|
if e.Type&h.types == e.Type {
|
||||||
foremost = false
|
|
||||||
events.Add(k, e)
|
events.Add(k, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Deliver Enter events.
|
||||||
|
for _, k := range hits {
|
||||||
|
if _, found := searchTag(p.entered, k); found {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
h := q.handlers[k]
|
||||||
|
e.Type = pointer.Enter
|
||||||
|
e.Position = q.invTransform(h.area, e.Position)
|
||||||
|
|
||||||
|
if e.Type&h.types == e.Type {
|
||||||
|
events.Add(k, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.entered = append(p.entered[:0], hits...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func searchTag(tags []event.Tag, tag event.Tag) (int, bool) {
|
||||||
|
for i, t := range tags {
|
||||||
|
if t == tag {
|
||||||
|
return i, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (op *areaOp) Decode(d []byte) {
|
func (op *areaOp) Decode(d []byte) {
|
||||||
|
|||||||
@@ -206,6 +206,22 @@ func TestPointerEnterLeave(t *testing.T) {
|
|||||||
assertEventSequence(t, r.Events(handler1))
|
assertEventSequence(t, r.Events(handler1))
|
||||||
assertEventSequence(t, r.Events(handler2), pointer.Enter, pointer.Press)
|
assertEventSequence(t, r.Events(handler2), pointer.Enter, pointer.Press)
|
||||||
|
|
||||||
|
// Check that a drag only affects the participating handlers.
|
||||||
|
r.Add(
|
||||||
|
// Leave
|
||||||
|
pointer.Event{
|
||||||
|
Type: pointer.Move,
|
||||||
|
Position: f32.Pt(25, 25),
|
||||||
|
},
|
||||||
|
// Enter
|
||||||
|
pointer.Event{
|
||||||
|
Type: pointer.Move,
|
||||||
|
Position: f32.Pt(50, 50),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assertEventSequence(t, r.Events(handler1))
|
||||||
|
assertEventSequence(t, r.Events(handler2), pointer.Leave, pointer.Drag, pointer.Enter, pointer.Drag)
|
||||||
|
|
||||||
// Check that a Release event generates Enter/Leave Events.
|
// Check that a Release event generates Enter/Leave Events.
|
||||||
r.Add(
|
r.Add(
|
||||||
pointer.Event{
|
pointer.Event{
|
||||||
@@ -220,6 +236,34 @@ func TestPointerEnterLeave(t *testing.T) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMultipleAreas(t *testing.T) {
|
||||||
|
handler := new(int)
|
||||||
|
|
||||||
|
var ops op.Ops
|
||||||
|
|
||||||
|
addPointerHandler(&ops, handler, image.Rect(0, 0, 100, 100))
|
||||||
|
addPointerHandler(&ops, handler, image.Rect(50, 50, 200, 200))
|
||||||
|
|
||||||
|
var r Router
|
||||||
|
r.Frame(&ops)
|
||||||
|
// Hit first area, then second area, then both.
|
||||||
|
r.Add(
|
||||||
|
pointer.Event{
|
||||||
|
Type: pointer.Move,
|
||||||
|
Position: f32.Pt(25, 25),
|
||||||
|
},
|
||||||
|
pointer.Event{
|
||||||
|
Type: pointer.Move,
|
||||||
|
Position: f32.Pt(150, 150),
|
||||||
|
},
|
||||||
|
pointer.Event{
|
||||||
|
Type: pointer.Move,
|
||||||
|
Position: f32.Pt(50, 50),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assertEventSequence(t, r.Events(handler), pointer.Cancel, pointer.Enter, pointer.Move, pointer.Move, pointer.Move)
|
||||||
|
}
|
||||||
|
|
||||||
func TestPointerEnterLeaveNested(t *testing.T) {
|
func TestPointerEnterLeaveNested(t *testing.T) {
|
||||||
handler1 := new(int)
|
handler1 := new(int)
|
||||||
handler2 := new(int)
|
handler2 := new(int)
|
||||||
|
|||||||
Reference in New Issue
Block a user