gioui.org/ui@v0.0.0-20190926171558-ce74bc0cbaea/app/internal/input/pointer.go (about) 1 // SPDX-License-Identifier: Unlicense OR MIT 2 3 package input 4 5 import ( 6 "encoding/binary" 7 "image" 8 9 "gioui.org/ui" 10 "gioui.org/ui/f32" 11 "gioui.org/ui/internal/opconst" 12 "gioui.org/ui/internal/ops" 13 "gioui.org/ui/pointer" 14 ) 15 16 type pointerQueue struct { 17 hitTree []hitNode 18 areas []areaNode 19 handlers map[ui.Key]*pointerHandler 20 pointers []pointerInfo 21 reader ops.Reader 22 scratch []ui.Key 23 } 24 25 type hitNode struct { 26 next int 27 area int 28 // Pass tracks the most recent PassOp mode. 29 pass bool 30 31 // For handler nodes. 32 key ui.Key 33 } 34 35 type pointerInfo struct { 36 id pointer.ID 37 pressed bool 38 handlers []ui.Key 39 } 40 41 type pointerHandler struct { 42 area int 43 active bool 44 transform ui.TransformOp 45 wantsGrab bool 46 } 47 48 type areaOp struct { 49 kind areaKind 50 rect image.Rectangle 51 } 52 53 type areaNode struct { 54 trans ui.TransformOp 55 next int 56 area areaOp 57 } 58 59 type areaKind uint8 60 61 const ( 62 areaRect areaKind = iota 63 areaEllipse 64 ) 65 66 func (q *pointerQueue) collectHandlers(r *ops.Reader, events *handlerEvents, t ui.TransformOp, area, node int, pass bool) { 67 for encOp, ok := r.Decode(); ok; encOp, ok = r.Decode() { 68 switch opconst.OpType(encOp.Data[0]) { 69 case opconst.TypePush: 70 q.collectHandlers(r, events, t, area, node, pass) 71 case opconst.TypePop: 72 return 73 case opconst.TypePass: 74 op := decodePassOp(encOp.Data) 75 pass = op.Pass 76 case opconst.TypeArea: 77 var op areaOp 78 op.Decode(encOp.Data) 79 q.areas = append(q.areas, areaNode{trans: t, next: area, area: op}) 80 area = len(q.areas) - 1 81 q.hitTree = append(q.hitTree, hitNode{ 82 next: node, 83 area: area, 84 pass: pass, 85 }) 86 node = len(q.hitTree) - 1 87 case opconst.TypeTransform: 88 op := ops.DecodeTransformOp(encOp.Data) 89 t = t.Multiply(ui.TransformOp(op)) 90 case opconst.TypePointerInput: 91 op := decodePointerInputOp(encOp.Data, encOp.Refs) 92 q.hitTree = append(q.hitTree, hitNode{ 93 next: node, 94 area: area, 95 pass: pass, 96 key: op.Key, 97 }) 98 node = len(q.hitTree) - 1 99 h, ok := q.handlers[op.Key] 100 if !ok { 101 h = new(pointerHandler) 102 q.handlers[op.Key] = h 103 events.Set(op.Key, []ui.Event{pointer.Event{Type: pointer.Cancel}}) 104 } 105 h.active = true 106 h.area = area 107 h.transform = t 108 h.wantsGrab = h.wantsGrab || op.Grab 109 } 110 } 111 } 112 113 func (q *pointerQueue) opHit(handlers *[]ui.Key, pos f32.Point) { 114 // Track whether we're passing through hits. 115 pass := true 116 idx := len(q.hitTree) - 1 117 for idx >= 0 { 118 n := &q.hitTree[idx] 119 if !q.hit(n.area, pos) { 120 idx-- 121 continue 122 } 123 pass = pass && n.pass 124 if pass { 125 idx-- 126 } else { 127 idx = n.next 128 } 129 if n.key != nil { 130 if _, exists := q.handlers[n.key]; exists { 131 *handlers = append(*handlers, n.key) 132 } 133 134 } 135 } 136 } 137 138 func (q *pointerQueue) hit(areaIdx int, p f32.Point) bool { 139 for areaIdx != -1 { 140 a := &q.areas[areaIdx] 141 if !a.hit(p) { 142 return false 143 } 144 areaIdx = a.next 145 } 146 return true 147 } 148 149 func (a *areaNode) hit(p f32.Point) bool { 150 p = a.trans.Invert().Transform(p) 151 return a.area.Hit(p) 152 } 153 154 func (q *pointerQueue) init() { 155 if q.handlers == nil { 156 q.handlers = make(map[ui.Key]*pointerHandler) 157 } 158 } 159 160 func (q *pointerQueue) Frame(root *ui.Ops, events *handlerEvents) { 161 q.init() 162 for _, h := range q.handlers { 163 // Reset handler. 164 h.active = false 165 } 166 q.hitTree = q.hitTree[:0] 167 q.areas = q.areas[:0] 168 q.reader.Reset(root) 169 q.collectHandlers(&q.reader, events, ui.TransformOp{}, -1, -1, false) 170 for k, h := range q.handlers { 171 if !h.active { 172 q.dropHandler(k) 173 delete(q.handlers, k) 174 } 175 } 176 } 177 178 func (q *pointerQueue) dropHandler(k ui.Key) { 179 for i := range q.pointers { 180 p := &q.pointers[i] 181 for i := len(p.handlers) - 1; i >= 0; i-- { 182 if p.handlers[i] == k { 183 p.handlers = append(p.handlers[:i], p.handlers[i+1:]...) 184 } 185 } 186 } 187 } 188 189 func (q *pointerQueue) Push(e pointer.Event, events *handlerEvents) { 190 q.init() 191 if e.Type == pointer.Cancel { 192 q.pointers = q.pointers[:0] 193 for k := range q.handlers { 194 q.dropHandler(k) 195 } 196 return 197 } 198 pidx := -1 199 for i, p := range q.pointers { 200 if p.id == e.PointerID { 201 pidx = i 202 break 203 } 204 } 205 if pidx == -1 { 206 q.pointers = append(q.pointers, pointerInfo{id: e.PointerID}) 207 pidx = len(q.pointers) - 1 208 } 209 p := &q.pointers[pidx] 210 if !p.pressed && (e.Type == pointer.Move || e.Type == pointer.Press) { 211 p.handlers, q.scratch = q.scratch[:0], p.handlers 212 q.opHit(&p.handlers, e.Position) 213 if e.Type == pointer.Press { 214 p.pressed = true 215 } 216 } 217 if p.pressed { 218 // Resolve grabs. 219 q.scratch = q.scratch[:0] 220 for i, k := range p.handlers { 221 h := q.handlers[k] 222 if h.wantsGrab { 223 q.scratch = append(q.scratch, p.handlers[:i]...) 224 q.scratch = append(q.scratch, p.handlers[i+1:]...) 225 break 226 } 227 } 228 // Drop handlers that lost their grab. 229 for _, k := range q.scratch { 230 q.dropHandler(k) 231 } 232 } 233 if e.Type == pointer.Release { 234 q.pointers = append(q.pointers[:pidx], q.pointers[pidx+1:]...) 235 } 236 for i, k := range p.handlers { 237 h := q.handlers[k] 238 e := e 239 switch { 240 case p.pressed && len(p.handlers) == 1: 241 e.Priority = pointer.Grabbed 242 case i == 0: 243 e.Priority = pointer.Foremost 244 } 245 e.Hit = q.hit(h.area, e.Position) 246 e.Position = h.transform.Invert().Transform(e.Position) 247 events.Add(k, e) 248 if e.Type == pointer.Release { 249 // Release grab when the number of grabs reaches zero. 250 grabs := 0 251 for _, p := range q.pointers { 252 if p.pressed && len(p.handlers) == 1 && p.handlers[0] == k { 253 grabs++ 254 } 255 } 256 if grabs == 0 { 257 h.wantsGrab = false 258 } 259 } 260 } 261 } 262 263 func (op *areaOp) Decode(d []byte) { 264 if opconst.OpType(d[0]) != opconst.TypeArea { 265 panic("invalid op") 266 } 267 bo := binary.LittleEndian 268 rect := image.Rectangle{ 269 Min: image.Point{ 270 X: int(int32(bo.Uint32(d[2:]))), 271 Y: int(int32(bo.Uint32(d[6:]))), 272 }, 273 Max: image.Point{ 274 X: int(int32(bo.Uint32(d[10:]))), 275 Y: int(int32(bo.Uint32(d[14:]))), 276 }, 277 } 278 *op = areaOp{ 279 kind: areaKind(d[1]), 280 rect: rect, 281 } 282 } 283 284 func (op *areaOp) Hit(pos f32.Point) bool { 285 min := f32.Point{ 286 X: float32(op.rect.Min.X), 287 Y: float32(op.rect.Min.Y), 288 } 289 pos = pos.Sub(min) 290 size := op.rect.Size() 291 switch op.kind { 292 case areaRect: 293 if 0 <= pos.X && pos.X < float32(size.X) && 294 0 <= pos.Y && pos.Y < float32(size.Y) { 295 return true 296 } else { 297 return false 298 } 299 case areaEllipse: 300 rx := float32(size.X) / 2 301 ry := float32(size.Y) / 2 302 rx2 := rx * rx 303 ry2 := ry * ry 304 xh := pos.X - rx 305 yk := pos.Y - ry 306 if xh*xh*ry2+yk*yk*rx2 <= rx2*ry2 { 307 return true 308 } else { 309 return false 310 } 311 default: 312 panic("invalid area kind") 313 } 314 } 315 316 func decodePointerInputOp(d []byte, refs []interface{}) pointer.InputOp { 317 if opconst.OpType(d[0]) != opconst.TypePointerInput { 318 panic("invalid op") 319 } 320 return pointer.InputOp{ 321 Grab: d[1] != 0, 322 Key: refs[0].(ui.Key), 323 } 324 } 325 326 func decodePassOp(d []byte) pointer.PassOp { 327 if opconst.OpType(d[0]) != opconst.TypePass { 328 panic("invalid op") 329 } 330 return pointer.PassOp{ 331 Pass: d[1] != 0, 332 } 333 }