forked from joejulian/gio
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
|
||||
pointers []pointerInfo
|
||||
reader ops.Reader
|
||||
|
||||
scratch []event.Tag
|
||||
}
|
||||
|
||||
type hitNode struct {
|
||||
@@ -240,18 +242,29 @@ func (q *pointerQueue) Push(e pointer.Event, events *handlerEvents) {
|
||||
e.Type = pointer.Drag
|
||||
}
|
||||
|
||||
q.deliverEnterLeaveEvents(p, events, e)
|
||||
if e.Type == pointer.Release {
|
||||
q.deliverEvent(p, events, e)
|
||||
p.pressed = false
|
||||
}
|
||||
if !p.pressed {
|
||||
if e.Type == pointer.Press {
|
||||
p.pressed = true
|
||||
q.scratch = q.scratch[:0]
|
||||
q.opHit(&q.scratch, e.Position)
|
||||
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, events, e)
|
||||
}
|
||||
q.deliverEnterLeaveEvents(p, q.scratch, 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 {
|
||||
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) {
|
||||
foremost := true
|
||||
for _, k := range p.handlers {
|
||||
h := q.handlers[k]
|
||||
e := e
|
||||
if p.pressed && len(p.handlers) == 1 {
|
||||
e.Priority = pointer.Grabbed
|
||||
} else if foremost {
|
||||
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:
|
||||
func (q *pointerQueue) deliverEnterLeaveEvents(p *pointerInfo, hits []event.Tag, events *handlerEvents, e pointer.Event) {
|
||||
if e.Source != pointer.Mouse && !p.pressed {
|
||||
// Consider non-mouse pointers leaving when they're released.
|
||||
hits = nil
|
||||
}
|
||||
// Deliver Leave events.
|
||||
for _, k := range p.entered {
|
||||
if _, found := searchTag(hits, k); found {
|
||||
continue
|
||||
}
|
||||
h := q.handlers[k]
|
||||
e.Type = pointer.Leave
|
||||
e.Position = q.invTransform(h.area, e.Position)
|
||||
|
||||
if e.Type&h.types == e.Type {
|
||||
foremost = false
|
||||
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) {
|
||||
|
||||
@@ -206,6 +206,22 @@ func TestPointerEnterLeave(t *testing.T) {
|
||||
assertEventSequence(t, r.Events(handler1))
|
||||
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.
|
||||
r.Add(
|
||||
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) {
|
||||
handler1 := new(int)
|
||||
handler2 := new(int)
|
||||
|
||||
Reference in New Issue
Block a user