gioui.org@v0.6.1-0.20240506124620-7a9ce51988ce/io/input/pointer.go (about) 1 // SPDX-License-Identifier: Unlicense OR MIT 2 3 package input 4 5 import ( 6 "image" 7 "io" 8 9 "gioui.org/f32" 10 f32internal "gioui.org/internal/f32" 11 "gioui.org/internal/ops" 12 "gioui.org/io/event" 13 "gioui.org/io/pointer" 14 "gioui.org/io/semantic" 15 "gioui.org/io/system" 16 "gioui.org/io/transfer" 17 ) 18 19 type pointerQueue struct { 20 hitTree []hitNode 21 areas []areaNode 22 23 semantic struct { 24 idsAssigned bool 25 lastID SemanticID 26 // contentIDs maps semantic content to a list of semantic IDs 27 // previously assigned. It is used to maintain stable IDs across 28 // frames. 29 contentIDs map[semanticContent][]semanticID 30 } 31 } 32 33 type hitNode struct { 34 next int 35 area int 36 37 // For handler nodes. 38 tag event.Tag 39 pass bool 40 } 41 42 // pointerState is the input state related to pointer events. 43 type pointerState struct { 44 cursor pointer.Cursor 45 pointers []pointerInfo 46 } 47 48 type pointerInfo struct { 49 id pointer.ID 50 pressed bool 51 handlers []event.Tag 52 // last tracks the last pointer event received, 53 // used while processing frame events. 54 last pointer.Event 55 56 // entered tracks the tags that contain the pointer. 57 entered []event.Tag 58 59 dataSource event.Tag // dragging source tag 60 dataTarget event.Tag // dragging target tag 61 } 62 63 type pointerHandler struct { 64 // areaPlusOne is the index into the list of pointerQueue.areas, plus 1. 65 areaPlusOne int 66 // setup tracks whether the handler has received 67 // the pointer.Cancel event that resets its state. 68 setup bool 69 } 70 71 // pointerFilter represents the union of a set of pointer filters. 72 type pointerFilter struct { 73 kinds pointer.Kind 74 // min and max horizontal/vertical scroll 75 scrollX, scrollY pointer.ScrollRange 76 77 sourceMimes []string 78 targetMimes []string 79 } 80 81 type areaOp struct { 82 kind areaKind 83 rect image.Rectangle 84 } 85 86 type areaNode struct { 87 trans f32.Affine2D 88 area areaOp 89 90 cursor pointer.Cursor 91 92 // Tree indices, with -1 being the sentinel. 93 parent int 94 firstChild int 95 lastChild int 96 sibling int 97 98 semantic struct { 99 valid bool 100 id SemanticID 101 content semanticContent 102 } 103 action system.Action 104 } 105 106 type areaKind uint8 107 108 // collectState represents the state for pointerCollector. 109 type collectState struct { 110 t f32.Affine2D 111 // nodePlusOne is the current node index, plus one to 112 // make the zero value collectState the initial state. 113 nodePlusOne int 114 pass int 115 } 116 117 // pointerCollector tracks the state needed to update an pointerQueue 118 // from pointer ops. 119 type pointerCollector struct { 120 q *pointerQueue 121 state collectState 122 nodeStack []int 123 } 124 125 type semanticContent struct { 126 tag event.Tag 127 label string 128 desc string 129 class semantic.ClassOp 130 gestures SemanticGestures 131 selected bool 132 disabled bool 133 } 134 135 type semanticID struct { 136 id SemanticID 137 used bool 138 } 139 140 const ( 141 areaRect areaKind = iota 142 areaEllipse 143 ) 144 145 func (c *pointerCollector) resetState() { 146 c.state = collectState{} 147 c.nodeStack = c.nodeStack[:0] 148 // Pop every node except the root. 149 if len(c.q.hitTree) > 0 { 150 c.state.nodePlusOne = 0 + 1 151 } 152 } 153 154 func (c *pointerCollector) setTrans(t f32.Affine2D) { 155 c.state.t = t 156 } 157 158 func (c *pointerCollector) clip(op ops.ClipOp) { 159 kind := areaRect 160 if op.Shape == ops.Ellipse { 161 kind = areaEllipse 162 } 163 c.pushArea(kind, op.Bounds) 164 } 165 166 func (c *pointerCollector) pushArea(kind areaKind, bounds image.Rectangle) { 167 parentID := c.currentArea() 168 areaID := len(c.q.areas) 169 areaOp := areaOp{kind: kind, rect: bounds} 170 if parentID != -1 { 171 parent := &c.q.areas[parentID] 172 if parent.firstChild == -1 { 173 parent.firstChild = areaID 174 } 175 if siblingID := parent.lastChild; siblingID != -1 { 176 c.q.areas[siblingID].sibling = areaID 177 } 178 parent.lastChild = areaID 179 } 180 an := areaNode{ 181 trans: c.state.t, 182 area: areaOp, 183 parent: parentID, 184 sibling: -1, 185 firstChild: -1, 186 lastChild: -1, 187 } 188 189 c.q.areas = append(c.q.areas, an) 190 c.nodeStack = append(c.nodeStack, c.state.nodePlusOne-1) 191 c.addHitNode(hitNode{ 192 area: areaID, 193 pass: true, 194 }) 195 } 196 197 func (c *pointerCollector) popArea() { 198 n := len(c.nodeStack) 199 c.state.nodePlusOne = c.nodeStack[n-1] + 1 200 c.nodeStack = c.nodeStack[:n-1] 201 } 202 203 func (c *pointerCollector) pass() { 204 c.state.pass++ 205 } 206 207 func (c *pointerCollector) popPass() { 208 c.state.pass-- 209 } 210 211 func (c *pointerCollector) currentArea() int { 212 if i := c.state.nodePlusOne - 1; i != -1 { 213 n := c.q.hitTree[i] 214 return n.area 215 } 216 return -1 217 } 218 219 func (c *pointerCollector) currentAreaBounds() image.Rectangle { 220 a := c.currentArea() 221 if a == -1 { 222 panic("no root area") 223 } 224 return c.q.areas[a].bounds() 225 } 226 227 func (c *pointerCollector) addHitNode(n hitNode) { 228 n.next = c.state.nodePlusOne - 1 229 c.q.hitTree = append(c.q.hitTree, n) 230 c.state.nodePlusOne = len(c.q.hitTree) - 1 + 1 231 } 232 233 // newHandler returns the current handler or a new one for tag. 234 func (c *pointerCollector) newHandler(tag event.Tag, state *pointerHandler) { 235 areaID := c.currentArea() 236 c.addHitNode(hitNode{ 237 area: areaID, 238 tag: tag, 239 pass: c.state.pass > 0, 240 }) 241 state.areaPlusOne = areaID + 1 242 } 243 244 func (s *pointerHandler) Reset() { 245 s.areaPlusOne = 0 246 } 247 248 func (c *pointerCollector) actionInputOp(act system.Action) { 249 areaID := c.currentArea() 250 area := &c.q.areas[areaID] 251 area.action = act 252 } 253 254 func (q *pointerQueue) grab(state pointerState, req pointer.GrabCmd) (pointerState, []taggedEvent) { 255 var evts []taggedEvent 256 for _, p := range state.pointers { 257 if !p.pressed || p.id != req.ID { 258 continue 259 } 260 // Drop other handlers that lost their grab. 261 for i := len(p.handlers) - 1; i >= 0; i-- { 262 if tag := p.handlers[i]; tag != req.Tag { 263 evts = append(evts, taggedEvent{ 264 tag: tag, 265 event: pointer.Event{Kind: pointer.Cancel}, 266 }) 267 state = dropHandler(state, tag) 268 } 269 } 270 break 271 } 272 return state, evts 273 } 274 275 func (c *pointerCollector) inputOp(tag event.Tag, state *pointerHandler) { 276 areaID := c.currentArea() 277 area := &c.q.areas[areaID] 278 area.semantic.content.tag = tag 279 c.newHandler(tag, state) 280 } 281 282 func (p *pointerFilter) Add(f event.Filter) { 283 switch f := f.(type) { 284 case transfer.SourceFilter: 285 for _, m := range p.sourceMimes { 286 if m == f.Type { 287 return 288 } 289 } 290 p.sourceMimes = append(p.sourceMimes, f.Type) 291 case transfer.TargetFilter: 292 for _, m := range p.targetMimes { 293 if m == f.Type { 294 return 295 } 296 } 297 p.targetMimes = append(p.targetMimes, f.Type) 298 case pointer.Filter: 299 p.kinds = p.kinds | f.Kinds 300 p.scrollX = p.scrollX.Union(f.ScrollX) 301 p.scrollY = p.scrollY.Union(f.ScrollY) 302 } 303 } 304 305 func (p *pointerFilter) Matches(e event.Event) bool { 306 switch e := e.(type) { 307 case pointer.Event: 308 return e.Kind&p.kinds == e.Kind 309 case transfer.CancelEvent, transfer.InitiateEvent: 310 return len(p.sourceMimes) > 0 || len(p.targetMimes) > 0 311 case transfer.RequestEvent: 312 for _, t := range p.sourceMimes { 313 if t == e.Type { 314 return true 315 } 316 } 317 case transfer.DataEvent: 318 for _, t := range p.targetMimes { 319 if t == e.Type { 320 return true 321 } 322 } 323 } 324 return false 325 } 326 327 func (p *pointerFilter) Merge(p2 pointerFilter) { 328 p.kinds = p.kinds | p2.kinds 329 p.scrollX = p.scrollX.Union(p2.scrollX) 330 p.scrollY = p.scrollY.Union(p2.scrollY) 331 p.sourceMimes = append(p.sourceMimes, p2.sourceMimes...) 332 p.targetMimes = append(p.targetMimes, p2.targetMimes...) 333 } 334 335 // clampScroll splits a scroll distance in the remaining scroll and the 336 // scroll accepted by the filter. 337 func (p *pointerFilter) clampScroll(scroll f32.Point) (left, scrolled f32.Point) { 338 left.X, scrolled.X = clampSplit(scroll.X, p.scrollX.Min, p.scrollX.Max) 339 left.Y, scrolled.Y = clampSplit(scroll.Y, p.scrollY.Min, p.scrollY.Max) 340 return 341 } 342 343 func clampSplit(v float32, min, max int) (float32, float32) { 344 if m := float32(max); v > m { 345 return v - m, m 346 } 347 if m := float32(min); v < m { 348 return v - m, m 349 } 350 return 0, v 351 } 352 353 func (s *pointerHandler) ResetEvent() (event.Event, bool) { 354 if s.setup { 355 return nil, false 356 } 357 s.setup = true 358 return pointer.Event{Kind: pointer.Cancel}, true 359 } 360 361 func (c *pointerCollector) semanticLabel(lbl string) { 362 areaID := c.currentArea() 363 area := &c.q.areas[areaID] 364 area.semantic.valid = true 365 area.semantic.content.label = lbl 366 } 367 368 func (c *pointerCollector) semanticDesc(desc string) { 369 areaID := c.currentArea() 370 area := &c.q.areas[areaID] 371 area.semantic.valid = true 372 area.semantic.content.desc = desc 373 } 374 375 func (c *pointerCollector) semanticClass(class semantic.ClassOp) { 376 areaID := c.currentArea() 377 area := &c.q.areas[areaID] 378 area.semantic.valid = true 379 area.semantic.content.class = class 380 } 381 382 func (c *pointerCollector) semanticSelected(selected bool) { 383 areaID := c.currentArea() 384 area := &c.q.areas[areaID] 385 area.semantic.valid = true 386 area.semantic.content.selected = selected 387 } 388 389 func (c *pointerCollector) semanticEnabled(enabled bool) { 390 areaID := c.currentArea() 391 area := &c.q.areas[areaID] 392 area.semantic.valid = true 393 area.semantic.content.disabled = !enabled 394 } 395 396 func (c *pointerCollector) cursor(cursor pointer.Cursor) { 397 areaID := c.currentArea() 398 area := &c.q.areas[areaID] 399 area.cursor = cursor 400 } 401 402 func (q *pointerQueue) offerData(handlers map[event.Tag]*handler, state pointerState, req transfer.OfferCmd) (pointerState, []taggedEvent) { 403 var evts []taggedEvent 404 for i, p := range state.pointers { 405 if p.dataSource != req.Tag { 406 continue 407 } 408 if p.dataTarget != nil { 409 evts = append(evts, taggedEvent{tag: p.dataTarget, event: transfer.DataEvent{ 410 Type: req.Type, 411 Open: func() io.ReadCloser { 412 return req.Data 413 }, 414 }}) 415 } 416 state.pointers = append([]pointerInfo{}, state.pointers...) 417 state.pointers[i], evts = q.deliverTransferCancelEvent(handlers, p, evts) 418 break 419 } 420 return state, evts 421 } 422 423 func (c *pointerCollector) Reset() { 424 c.q.reset() 425 c.resetState() 426 c.ensureRoot() 427 } 428 429 // Ensure implicit root area for semantic descriptions to hang onto. 430 func (c *pointerCollector) ensureRoot() { 431 if len(c.q.areas) > 0 { 432 return 433 } 434 c.pushArea(areaRect, image.Rect(-1e6, -1e6, 1e6, 1e6)) 435 // Make it semantic to ensure a single semantic root. 436 c.q.areas[0].semantic.valid = true 437 } 438 439 func (q *pointerQueue) assignSemIDs() { 440 if q.semantic.idsAssigned { 441 return 442 } 443 q.semantic.idsAssigned = true 444 for i, a := range q.areas { 445 if a.semantic.valid { 446 q.areas[i].semantic.id = q.semanticIDFor(a.semantic.content) 447 } 448 } 449 } 450 451 func (q *pointerQueue) AppendSemantics(nodes []SemanticNode) []SemanticNode { 452 q.assignSemIDs() 453 nodes = q.appendSemanticChildren(nodes, 0) 454 nodes = q.appendSemanticArea(nodes, 0, 0) 455 return nodes 456 } 457 458 func (q *pointerQueue) appendSemanticArea(nodes []SemanticNode, parentID SemanticID, nodeIdx int) []SemanticNode { 459 areaIdx := nodes[nodeIdx].areaIdx 460 a := q.areas[areaIdx] 461 childStart := len(nodes) 462 nodes = q.appendSemanticChildren(nodes, a.firstChild) 463 childEnd := len(nodes) 464 for i := childStart; i < childEnd; i++ { 465 nodes = q.appendSemanticArea(nodes, a.semantic.id, i) 466 } 467 n := &nodes[nodeIdx] 468 n.ParentID = parentID 469 n.Children = nodes[childStart:childEnd] 470 return nodes 471 } 472 473 func (q *pointerQueue) appendSemanticChildren(nodes []SemanticNode, areaIdx int) []SemanticNode { 474 if areaIdx == -1 { 475 return nodes 476 } 477 a := q.areas[areaIdx] 478 if semID := a.semantic.id; semID != 0 { 479 cnt := a.semantic.content 480 nodes = append(nodes, SemanticNode{ 481 ID: semID, 482 Desc: SemanticDesc{ 483 Bounds: a.bounds(), 484 Label: cnt.label, 485 Description: cnt.desc, 486 Class: cnt.class, 487 Gestures: cnt.gestures, 488 Selected: cnt.selected, 489 Disabled: cnt.disabled, 490 }, 491 areaIdx: areaIdx, 492 }) 493 } else { 494 nodes = q.appendSemanticChildren(nodes, a.firstChild) 495 } 496 return q.appendSemanticChildren(nodes, a.sibling) 497 } 498 499 func (q *pointerQueue) semanticIDFor(content semanticContent) SemanticID { 500 ids := q.semantic.contentIDs[content] 501 for i, id := range ids { 502 if !id.used { 503 ids[i].used = true 504 return id.id 505 } 506 } 507 // No prior assigned ID; allocate a new one. 508 q.semantic.lastID++ 509 id := semanticID{id: q.semantic.lastID, used: true} 510 if q.semantic.contentIDs == nil { 511 q.semantic.contentIDs = make(map[semanticContent][]semanticID) 512 } 513 q.semantic.contentIDs[content] = append(q.semantic.contentIDs[content], id) 514 return id.id 515 } 516 517 func (q *pointerQueue) ActionAt(pos f32.Point) (action system.Action, hasAction bool) { 518 q.hitTest(pos, func(n *hitNode) bool { 519 area := q.areas[n.area] 520 if area.action != 0 { 521 action = area.action 522 hasAction = true 523 return false 524 } 525 return true 526 }) 527 return action, hasAction 528 } 529 530 func (q *pointerQueue) SemanticAt(pos f32.Point) (semID SemanticID, hasSemID bool) { 531 q.assignSemIDs() 532 q.hitTest(pos, func(n *hitNode) bool { 533 area := q.areas[n.area] 534 if area.semantic.id != 0 { 535 semID = area.semantic.id 536 hasSemID = true 537 return false 538 } 539 return true 540 }) 541 return semID, hasSemID 542 } 543 544 // hitTest searches the hit tree for nodes matching pos. Any node matching pos will 545 // have the onNode func invoked on it to allow the caller to extract whatever information 546 // is necessary for further processing. onNode may return false to terminate the walk of 547 // the hit tree, or true to continue. Providing this algorithm in this generic way 548 // allows normal event routing and system action event routing to share the same traversal 549 // logic even though they are interested in different aspects of hit nodes. 550 func (q *pointerQueue) hitTest(pos f32.Point, onNode func(*hitNode) bool) pointer.Cursor { 551 // Track whether we're passing through hits. 552 pass := true 553 idx := len(q.hitTree) - 1 554 cursor := pointer.CursorDefault 555 for idx >= 0 { 556 n := &q.hitTree[idx] 557 hit, c := q.hit(n.area, pos) 558 if !hit { 559 idx-- 560 continue 561 } 562 if cursor == pointer.CursorDefault { 563 cursor = c 564 } 565 pass = pass && n.pass 566 if pass { 567 idx-- 568 } else { 569 idx = n.next 570 } 571 if !onNode(n) { 572 break 573 } 574 } 575 return cursor 576 } 577 578 func (q *pointerQueue) invTransform(areaIdx int, p f32.Point) f32.Point { 579 if areaIdx == -1 { 580 return p 581 } 582 return q.areas[areaIdx].trans.Invert().Transform(p) 583 } 584 585 func (q *pointerQueue) hit(areaIdx int, p f32.Point) (bool, pointer.Cursor) { 586 c := pointer.CursorDefault 587 for areaIdx != -1 { 588 a := &q.areas[areaIdx] 589 if c == pointer.CursorDefault { 590 c = a.cursor 591 } 592 p := a.trans.Invert().Transform(p) 593 if !a.area.Hit(p) { 594 return false, c 595 } 596 areaIdx = a.parent 597 } 598 return true, c 599 } 600 601 func (q *pointerQueue) reset() { 602 q.hitTree = q.hitTree[:0] 603 q.areas = q.areas[:0] 604 q.semantic.idsAssigned = false 605 for k, ids := range q.semantic.contentIDs { 606 for i := len(ids) - 1; i >= 0; i-- { 607 if !ids[i].used { 608 ids = append(ids[:i], ids[i+1:]...) 609 } else { 610 ids[i].used = false 611 } 612 } 613 if len(ids) > 0 { 614 q.semantic.contentIDs[k] = ids 615 } else { 616 delete(q.semantic.contentIDs, k) 617 } 618 } 619 } 620 621 func (q *pointerQueue) Frame(handlers map[event.Tag]*handler, state pointerState) (pointerState, []taggedEvent) { 622 for _, h := range handlers { 623 if h.pointer.areaPlusOne != 0 { 624 area := &q.areas[h.pointer.areaPlusOne-1] 625 if h.filter.pointer.kinds&(pointer.Press|pointer.Release) != 0 { 626 area.semantic.content.gestures |= ClickGesture 627 } 628 if h.filter.pointer.kinds&pointer.Scroll != 0 { 629 area.semantic.content.gestures |= ScrollGesture 630 } 631 area.semantic.valid = area.semantic.content.gestures != 0 632 } 633 } 634 var evts []taggedEvent 635 for i, p := range state.pointers { 636 changed := false 637 p, evts, state.cursor, changed = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, p.last) 638 if changed { 639 state.pointers = append([]pointerInfo{}, state.pointers...) 640 state.pointers[i] = p 641 } 642 } 643 return state, evts 644 } 645 646 func dropHandler(state pointerState, tag event.Tag) pointerState { 647 pointers := state.pointers 648 state.pointers = nil 649 for _, p := range pointers { 650 handlers := p.handlers 651 p.handlers = nil 652 for _, h := range handlers { 653 if h != tag { 654 p.handlers = append(p.handlers, h) 655 } 656 } 657 entered := p.entered 658 p.entered = nil 659 for _, h := range entered { 660 if h != tag { 661 p.entered = append(p.entered, h) 662 } 663 } 664 state.pointers = append(state.pointers, p) 665 } 666 return state 667 } 668 669 // pointerOf returns the pointerInfo index corresponding to the pointer in e. 670 func (s pointerState) pointerOf(e pointer.Event) (pointerState, int) { 671 for i, p := range s.pointers { 672 if p.id == e.PointerID { 673 return s, i 674 } 675 } 676 n := len(s.pointers) 677 s.pointers = append(s.pointers[:n:n], pointerInfo{id: e.PointerID}) 678 return s, len(s.pointers) - 1 679 } 680 681 // Deliver is like Push, but delivers an event to a particular area. 682 func (q *pointerQueue) Deliver(handlers map[event.Tag]*handler, areaIdx int, e pointer.Event) []taggedEvent { 683 scroll := e.Scroll 684 idx := len(q.hitTree) - 1 685 // Locate first potential receiver. 686 for idx != -1 { 687 n := &q.hitTree[idx] 688 if n.area == areaIdx { 689 break 690 } 691 idx-- 692 } 693 var evts []taggedEvent 694 for idx != -1 { 695 n := &q.hitTree[idx] 696 idx = n.next 697 h, ok := handlers[n.tag] 698 if !ok || !h.filter.pointer.Matches(e) { 699 continue 700 } 701 e := e 702 if e.Kind == pointer.Scroll { 703 if scroll == (f32.Point{}) { 704 break 705 } 706 scroll, e.Scroll = h.filter.pointer.clampScroll(scroll) 707 } 708 e.Position = q.invTransform(h.pointer.areaPlusOne-1, e.Position) 709 evts = append(evts, taggedEvent{tag: n.tag, event: e}) 710 if e.Kind != pointer.Scroll { 711 break 712 } 713 } 714 return evts 715 } 716 717 // SemanticArea returns the sematic content for area, and its parent area. 718 func (q *pointerQueue) SemanticArea(areaIdx int) (semanticContent, int) { 719 for areaIdx != -1 { 720 a := &q.areas[areaIdx] 721 areaIdx = a.parent 722 if !a.semantic.valid { 723 continue 724 } 725 return a.semantic.content, areaIdx 726 } 727 return semanticContent{}, -1 728 } 729 730 func (q *pointerQueue) Push(handlers map[event.Tag]*handler, state pointerState, e pointer.Event) (pointerState, []taggedEvent) { 731 var evts []taggedEvent 732 if e.Kind == pointer.Cancel { 733 for k := range handlers { 734 evts = append(evts, taggedEvent{ 735 event: pointer.Event{Kind: pointer.Cancel}, 736 tag: k, 737 }) 738 } 739 state.pointers = nil 740 return state, evts 741 } 742 state, pidx := state.pointerOf(e) 743 p := state.pointers[pidx] 744 745 switch e.Kind { 746 case pointer.Press: 747 p, evts, state.cursor, _ = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, e) 748 p.pressed = true 749 evts = q.deliverEvent(handlers, p, evts, e) 750 case pointer.Move: 751 if p.pressed { 752 e.Kind = pointer.Drag 753 } 754 p, evts, state.cursor, _ = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, e) 755 evts = q.deliverEvent(handlers, p, evts, e) 756 if p.pressed { 757 p, evts = q.deliverDragEvent(handlers, p, evts) 758 } 759 case pointer.Release: 760 evts = q.deliverEvent(handlers, p, evts, e) 761 p.pressed = false 762 p, evts, state.cursor, _ = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, e) 763 p, evts = q.deliverDropEvent(handlers, p, evts) 764 case pointer.Scroll: 765 p, evts, state.cursor, _ = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, e) 766 evts = q.deliverEvent(handlers, p, evts, e) 767 default: 768 panic("unsupported pointer event type") 769 } 770 771 p.last = e 772 773 if !p.pressed && len(p.entered) == 0 { 774 // No longer need to track pointer. 775 state.pointers = append(state.pointers[:pidx:pidx], state.pointers[pidx+1:]...) 776 } else { 777 state.pointers = append([]pointerInfo{}, state.pointers...) 778 state.pointers[pidx] = p 779 } 780 return state, evts 781 } 782 783 func (q *pointerQueue) deliverEvent(handlers map[event.Tag]*handler, p pointerInfo, evts []taggedEvent, e pointer.Event) []taggedEvent { 784 foremost := true 785 if p.pressed && len(p.handlers) == 1 { 786 e.Priority = pointer.Grabbed 787 foremost = false 788 } 789 scroll := e.Scroll 790 for _, k := range p.handlers { 791 h, ok := handlers[k] 792 if !ok { 793 continue 794 } 795 f := h.filter.pointer 796 if !f.Matches(e) { 797 continue 798 } 799 if e.Kind == pointer.Scroll { 800 if scroll == (f32.Point{}) { 801 return evts 802 } 803 scroll, e.Scroll = f.clampScroll(scroll) 804 } 805 e := e 806 if foremost { 807 foremost = false 808 e.Priority = pointer.Foremost 809 } 810 e.Position = q.invTransform(h.pointer.areaPlusOne-1, e.Position) 811 evts = append(evts, taggedEvent{event: e, tag: k}) 812 } 813 return evts 814 } 815 816 func (q *pointerQueue) deliverEnterLeaveEvents(handlers map[event.Tag]*handler, cursor pointer.Cursor, p pointerInfo, evts []taggedEvent, e pointer.Event) (pointerInfo, []taggedEvent, pointer.Cursor, bool) { 817 changed := false 818 var hits []event.Tag 819 if e.Source != pointer.Mouse && !p.pressed && e.Kind != pointer.Press { 820 // Consider non-mouse pointers leaving when they're released. 821 } else { 822 var transSrc *pointerFilter 823 if p.dataSource != nil { 824 transSrc = &handlers[p.dataSource].filter.pointer 825 } 826 cursor = q.hitTest(e.Position, func(n *hitNode) bool { 827 h, ok := handlers[n.tag] 828 if !ok { 829 return true 830 } 831 add := true 832 if p.pressed { 833 add = false 834 // Filter out non-participating handlers, 835 // except potential transfer targets when a transfer has been initiated. 836 if _, found := searchTag(p.handlers, n.tag); found { 837 add = true 838 } 839 if transSrc != nil { 840 if _, ok := firstMimeMatch(transSrc, &h.filter.pointer); ok { 841 add = true 842 } 843 } 844 } 845 if add { 846 hits = addHandler(hits, n.tag) 847 } 848 return true 849 }) 850 if !p.pressed { 851 changed = true 852 p.handlers = hits 853 } 854 } 855 // Deliver Leave events. 856 for _, k := range p.entered { 857 if _, found := searchTag(hits, k); found { 858 continue 859 } 860 h, ok := handlers[k] 861 if !ok { 862 continue 863 } 864 changed = true 865 e := e 866 e.Kind = pointer.Leave 867 868 if h.filter.pointer.Matches(e) { 869 e.Position = q.invTransform(h.pointer.areaPlusOne-1, e.Position) 870 evts = append(evts, taggedEvent{tag: k, event: e}) 871 } 872 } 873 // Deliver Enter events. 874 for _, k := range hits { 875 if _, found := searchTag(p.entered, k); found { 876 continue 877 } 878 h, ok := handlers[k] 879 if !ok { 880 continue 881 } 882 changed = true 883 e := e 884 e.Kind = pointer.Enter 885 886 if h.filter.pointer.Matches(e) { 887 e.Position = q.invTransform(h.pointer.areaPlusOne-1, e.Position) 888 evts = append(evts, taggedEvent{tag: k, event: e}) 889 } 890 } 891 p.entered = hits 892 return p, evts, cursor, changed 893 } 894 895 func (q *pointerQueue) deliverDragEvent(handlers map[event.Tag]*handler, p pointerInfo, evts []taggedEvent) (pointerInfo, []taggedEvent) { 896 if p.dataSource != nil { 897 return p, evts 898 } 899 // Identify the data source. 900 for _, k := range p.entered { 901 src := &handlers[k].filter.pointer 902 if len(src.sourceMimes) == 0 { 903 continue 904 } 905 // One data source handler per pointer. 906 p.dataSource = k 907 // Notify all potential targets. 908 for k, tgt := range handlers { 909 if _, ok := firstMimeMatch(src, &tgt.filter.pointer); ok { 910 evts = append(evts, taggedEvent{tag: k, event: transfer.InitiateEvent{}}) 911 } 912 } 913 break 914 } 915 return p, evts 916 } 917 918 func (q *pointerQueue) deliverDropEvent(handlers map[event.Tag]*handler, p pointerInfo, evts []taggedEvent) (pointerInfo, []taggedEvent) { 919 if p.dataSource == nil { 920 return p, evts 921 } 922 // Request data from the source. 923 src := &handlers[p.dataSource].filter.pointer 924 for _, k := range p.entered { 925 h := handlers[k] 926 if m, ok := firstMimeMatch(src, &h.filter.pointer); ok { 927 p.dataTarget = k 928 evts = append(evts, taggedEvent{tag: p.dataSource, event: transfer.RequestEvent{Type: m}}) 929 return p, evts 930 } 931 } 932 // No valid target found, abort. 933 return q.deliverTransferCancelEvent(handlers, p, evts) 934 } 935 936 func (q *pointerQueue) deliverTransferCancelEvent(handlers map[event.Tag]*handler, p pointerInfo, evts []taggedEvent) (pointerInfo, []taggedEvent) { 937 evts = append(evts, taggedEvent{tag: p.dataSource, event: transfer.CancelEvent{}}) 938 // Cancel all potential targets. 939 src := &handlers[p.dataSource].filter.pointer 940 for k, h := range handlers { 941 if _, ok := firstMimeMatch(src, &h.filter.pointer); ok { 942 evts = append(evts, taggedEvent{tag: k, event: transfer.CancelEvent{}}) 943 } 944 } 945 p.dataSource = nil 946 p.dataTarget = nil 947 return p, evts 948 } 949 950 // ClipFor clips r to the parents of area. 951 func (q *pointerQueue) ClipFor(area int, r image.Rectangle) image.Rectangle { 952 a := &q.areas[area] 953 parent := a.parent 954 for parent != -1 { 955 a := &q.areas[parent] 956 r = r.Intersect(a.bounds()) 957 parent = a.parent 958 } 959 return r 960 } 961 962 func searchTag(tags []event.Tag, tag event.Tag) (int, bool) { 963 for i, t := range tags { 964 if t == tag { 965 return i, true 966 } 967 } 968 return 0, false 969 } 970 971 // addHandler adds tag to the slice if not present. 972 func addHandler(tags []event.Tag, tag event.Tag) []event.Tag { 973 for _, t := range tags { 974 if t == tag { 975 return tags 976 } 977 } 978 return append(tags, tag) 979 } 980 981 // firstMimeMatch returns the first type match between src and tgt. 982 func firstMimeMatch(src, tgt *pointerFilter) (first string, matched bool) { 983 for _, m1 := range tgt.targetMimes { 984 for _, m2 := range src.sourceMimes { 985 if m1 == m2 { 986 return m1, true 987 } 988 } 989 } 990 return "", false 991 } 992 993 func (op *areaOp) Hit(pos f32.Point) bool { 994 pos = pos.Sub(f32internal.FPt(op.rect.Min)) 995 size := f32internal.FPt(op.rect.Size()) 996 switch op.kind { 997 case areaRect: 998 return 0 <= pos.X && pos.X < size.X && 999 0 <= pos.Y && pos.Y < size.Y 1000 case areaEllipse: 1001 rx := size.X / 2 1002 ry := size.Y / 2 1003 xh := pos.X - rx 1004 yk := pos.Y - ry 1005 // The ellipse function works in all cases because 1006 // 0/0 is not <= 1. 1007 return (xh*xh)/(rx*rx)+(yk*yk)/(ry*ry) <= 1 1008 default: 1009 panic("invalid area kind") 1010 } 1011 } 1012 1013 func (a *areaNode) bounds() image.Rectangle { 1014 return f32internal.Rectangle{ 1015 Min: a.trans.Transform(f32internal.FPt(a.area.rect.Min)), 1016 Max: a.trans.Transform(f32internal.FPt(a.area.rect.Max)), 1017 }.Round() 1018 }