ui/pointer: simplify hit testing

Serialize the implied tree of layers and handlers into a list
that can be traversed from the end to collect handlers.

This change will make it easier to switch op representation in
a follow-up change.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2019-04-26 19:22:59 +02:00
parent a8bb3c2f14
commit 7b6e1ce35a
+40 -41
View File
@@ -8,13 +8,19 @@ import (
) )
type Queue struct { type Queue struct {
// The root of the tree of ops relevant to pointer handling. hitTree []hitNode
root ui.Op
handlers map[Key]*handler handlers map[Key]*handler
pointers []pointerInfo pointers []pointerInfo
scratch []Key scratch []Key
} }
type hitNode struct {
// The layer depth.
level int
// The handler, or nil for a layer.
key Key
}
type pointerInfo struct { type pointerInfo struct {
id ID id ID
pressed bool pressed bool
@@ -33,12 +39,12 @@ type childOp interface {
ChildOp() ui.Op ChildOp() ui.Op
} }
func (q *Queue) collectHandlers(op ui.Op, t ui.Transform) ui.Op { func (q *Queue) collectHandlers(op ui.Op, t ui.Transform, layer int) ui.Op {
switch op := op.(type) { switch op := op.(type) {
case ui.Ops: case ui.Ops:
var all ui.Ops var all ui.Ops
for _, op := range op { for _, op := range op {
if op := q.collectHandlers(op, t); op != nil { if op := q.collectHandlers(op, t, layer); op != nil {
if ops, ok := op.(ui.Ops); ok { if ops, ok := op.(ui.Ops); ok {
all = append(all, ops...) all = append(all, ops...)
} else { } else {
@@ -48,14 +54,17 @@ func (q *Queue) collectHandlers(op ui.Op, t ui.Transform) ui.Op {
} }
return all return all
case ui.OpLayer: case ui.OpLayer:
child := q.collectHandlers(op.ChildOp(), t) layer++
q.hitTree = append(q.hitTree, hitNode{level: layer})
child := q.collectHandlers(op.ChildOp(), t, layer)
if child == nil { if child == nil {
return nil return nil
} }
return ui.OpLayer{Op: child} return ui.OpLayer{Op: child}
case ui.OpTransform: case ui.OpTransform:
return q.collectHandlers(op.ChildOp(), t.Mul(op.Transform)) return q.collectHandlers(op.ChildOp(), t.Mul(op.Transform), layer)
case OpHandler: case OpHandler:
q.hitTree = append(q.hitTree, hitNode{level: layer, key: op.Key})
h, ok := q.handlers[op.Key] h, ok := q.handlers[op.Key]
if !ok { if !ok {
h = new(handler) h = new(handler)
@@ -66,48 +75,37 @@ func (q *Queue) collectHandlers(op ui.Op, t ui.Transform) ui.Op {
h.wantsGrab = h.wantsGrab || op.Grab h.wantsGrab = h.wantsGrab || op.Grab
return op return op
case childOp: case childOp:
return q.collectHandlers(op.ChildOp(), t) return q.collectHandlers(op.ChildOp(), t, layer)
default: default:
return nil return nil
} }
} }
func (q *Queue) opHit(handlers *[]Key, op ui.Op, pos f32.Point) (HitResult, bool) { func (q *Queue) opHit(handlers *[]Key, pos f32.Point) {
if op == nil { level := 1 << 30
return HitNone, false opaque := false
} for i := len(q.hitTree) - 1; i >= 0; i-- {
switch op := op.(type) { n := q.hitTree[i]
case ui.Ops: if n.key == nil {
hitRes := HitNone // Layer
var layer bool if opaque {
for i := len(op) - 1; i >= 0; i-- { opaque = false
op := op[i] // Skip sibling handlers.
if _, ok := op.(ui.OpLayer); layer && ok { level = n.level - 1
}
} else if n.level <= level {
// Handler
h, ok := q.handlers[n.key]
if !ok {
continue continue
} }
res, l := q.opHit(handlers, op, pos) tpos := h.transform.InvTransform(pos)
if res > hitRes { res := h.area.Hit(tpos)
hitRes = res opaque = opaque || res == HitOpaque
if res != HitNone {
*handlers = append(*handlers, n.key)
} }
layer = layer || l
} }
return hitRes, layer
case ui.OpLayer:
res, layer := q.opHit(handlers, op.Op, pos)
return res, layer || res == HitOpaque
case OpHandler:
h, ok := q.handlers[op.Key]
if !ok {
return HitNone, false
}
tpos := h.transform.InvTransform(pos)
res := h.area.Hit(tpos)
if res != HitNone {
*handlers = append(*handlers, op.Key)
}
return res, false
default:
panic("unexpected op")
} }
} }
@@ -127,7 +125,8 @@ func (q *Queue) Frame(op ui.Op) {
h.events = h.events[:0] h.events = h.events[:0]
} }
} }
q.root = q.collectHandlers(op, ui.Transform{}) q.hitTree = q.hitTree[:0]
q.collectHandlers(op, ui.Transform{}, 0)
} }
func (q *Queue) For(k Key) []Event { func (q *Queue) For(k Key) []Event {
@@ -185,7 +184,7 @@ func (q *Queue) Push(e Event) {
p := &q.pointers[pidx] p := &q.pointers[pidx]
if !p.pressed && (e.Type == Move || e.Type == Press) { if !p.pressed && (e.Type == Move || e.Type == Press) {
p.handlers, q.scratch = q.scratch[:0], p.handlers p.handlers, q.scratch = q.scratch[:0], p.handlers
q.opHit(&p.handlers, q.root, e.Position) q.opHit(&p.handlers, e.Position)
// Drop handlers no longer hit. // Drop handlers no longer hit.
loop: loop:
for _, h := range q.scratch { for _, h := range q.scratch {