github.com/utopiagio/gio@v0.0.8/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 "github.com/utopiagio/gio/f32" 10 f32internal "github.com/utopiagio/gio/internal/f32" 11 "github.com/utopiagio/gio/internal/ops" 12 "github.com/utopiagio/gio/io/event" 13 "github.com/utopiagio/gio/io/pointer" 14 "github.com/utopiagio/gio/io/semantic" 15 "github.com/utopiagio/gio/io/system" 16 "github.com/utopiagio/gio/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 scrollRange image.Rectangle 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.scrollRange = p.scrollRange.Union(f.ScrollBounds) 301 } 302 } 303 304 func (p *pointerFilter) Matches(e event.Event) bool { 305 switch e := e.(type) { 306 case pointer.Event: 307 return e.Kind&p.kinds == e.Kind 308 case transfer.CancelEvent, transfer.InitiateEvent: 309 return len(p.sourceMimes) > 0 || len(p.targetMimes) > 0 310 case transfer.RequestEvent: 311 for _, t := range p.sourceMimes { 312 if t == e.Type { 313 return true 314 } 315 } 316 case transfer.DataEvent: 317 for _, t := range p.targetMimes { 318 if t == e.Type { 319 return true 320 } 321 } 322 } 323 return false 324 } 325 326 func (p *pointerFilter) Merge(p2 pointerFilter) { 327 p.kinds = p.kinds | p2.kinds 328 p.scrollRange = p.scrollRange.Union(p2.scrollRange) 329 p.sourceMimes = append(p.sourceMimes, p2.sourceMimes...) 330 p.targetMimes = append(p.targetMimes, p2.targetMimes...) 331 } 332 333 // clampScroll splits a scroll distance in the remaining scroll and the 334 // scroll accepted by the filter. 335 func (p *pointerFilter) clampScroll(scroll f32.Point) (left, scrolled f32.Point) { 336 left.X, scrolled.X = clampSplit(scroll.X, p.scrollRange.Min.X, p.scrollRange.Max.X) 337 left.Y, scrolled.Y = clampSplit(scroll.Y, p.scrollRange.Min.Y, p.scrollRange.Max.Y) 338 return 339 } 340 341 func clampSplit(v float32, min, max int) (float32, float32) { 342 if m := float32(max); v > m { 343 return v - m, m 344 } 345 if m := float32(min); v < m { 346 return v - m, m 347 } 348 return 0, v 349 } 350 351 func (s *pointerHandler) ResetEvent() (event.Event, bool) { 352 if s.setup { 353 return nil, false 354 } 355 s.setup = true 356 return pointer.Event{Kind: pointer.Cancel}, true 357 } 358 359 func (c *pointerCollector) semanticLabel(lbl string) { 360 areaID := c.currentArea() 361 area := &c.q.areas[areaID] 362 area.semantic.valid = true 363 area.semantic.content.label = lbl 364 } 365 366 func (c *pointerCollector) semanticDesc(desc string) { 367 areaID := c.currentArea() 368 area := &c.q.areas[areaID] 369 area.semantic.valid = true 370 area.semantic.content.desc = desc 371 } 372 373 func (c *pointerCollector) semanticClass(class semantic.ClassOp) { 374 areaID := c.currentArea() 375 area := &c.q.areas[areaID] 376 area.semantic.valid = true 377 area.semantic.content.class = class 378 } 379 380 func (c *pointerCollector) semanticSelected(selected bool) { 381 areaID := c.currentArea() 382 area := &c.q.areas[areaID] 383 area.semantic.valid = true 384 area.semantic.content.selected = selected 385 } 386 387 func (c *pointerCollector) semanticEnabled(enabled bool) { 388 areaID := c.currentArea() 389 area := &c.q.areas[areaID] 390 area.semantic.valid = true 391 area.semantic.content.disabled = !enabled 392 } 393 394 func (c *pointerCollector) cursor(cursor pointer.Cursor) { 395 areaID := c.currentArea() 396 area := &c.q.areas[areaID] 397 area.cursor = cursor 398 } 399 400 func (q *pointerQueue) offerData(handlers map[event.Tag]*handler, state pointerState, req transfer.OfferCmd) (pointerState, []taggedEvent) { 401 var evts []taggedEvent 402 for i, p := range state.pointers { 403 if p.dataSource != req.Tag { 404 continue 405 } 406 if p.dataTarget != nil { 407 evts = append(evts, taggedEvent{tag: p.dataTarget, event: transfer.DataEvent{ 408 Type: req.Type, 409 Open: func() io.ReadCloser { 410 return req.Data 411 }, 412 }}) 413 } 414 state.pointers = append([]pointerInfo{}, state.pointers...) 415 state.pointers[i], evts = q.deliverTransferCancelEvent(handlers, p, evts) 416 break 417 } 418 return state, evts 419 } 420 421 func (c *pointerCollector) Reset() { 422 c.q.reset() 423 c.resetState() 424 c.ensureRoot() 425 } 426 427 // Ensure implicit root area for semantic descriptions to hang onto. 428 func (c *pointerCollector) ensureRoot() { 429 if len(c.q.areas) > 0 { 430 return 431 } 432 c.pushArea(areaRect, image.Rect(-1e6, -1e6, 1e6, 1e6)) 433 // Make it semantic to ensure a single semantic root. 434 c.q.areas[0].semantic.valid = true 435 } 436 437 func (q *pointerQueue) assignSemIDs() { 438 if q.semantic.idsAssigned { 439 return 440 } 441 q.semantic.idsAssigned = true 442 for i, a := range q.areas { 443 if a.semantic.valid { 444 q.areas[i].semantic.id = q.semanticIDFor(a.semantic.content) 445 } 446 } 447 } 448 449 func (q *pointerQueue) AppendSemantics(nodes []SemanticNode) []SemanticNode { 450 q.assignSemIDs() 451 nodes = q.appendSemanticChildren(nodes, 0) 452 nodes = q.appendSemanticArea(nodes, 0, 0) 453 return nodes 454 } 455 456 func (q *pointerQueue) appendSemanticArea(nodes []SemanticNode, parentID SemanticID, nodeIdx int) []SemanticNode { 457 areaIdx := nodes[nodeIdx].areaIdx 458 a := q.areas[areaIdx] 459 childStart := len(nodes) 460 nodes = q.appendSemanticChildren(nodes, a.firstChild) 461 childEnd := len(nodes) 462 for i := childStart; i < childEnd; i++ { 463 nodes = q.appendSemanticArea(nodes, a.semantic.id, i) 464 } 465 n := &nodes[nodeIdx] 466 n.ParentID = parentID 467 n.Children = nodes[childStart:childEnd] 468 return nodes 469 } 470 471 func (q *pointerQueue) appendSemanticChildren(nodes []SemanticNode, areaIdx int) []SemanticNode { 472 if areaIdx == -1 { 473 return nodes 474 } 475 a := q.areas[areaIdx] 476 if semID := a.semantic.id; semID != 0 { 477 cnt := a.semantic.content 478 nodes = append(nodes, SemanticNode{ 479 ID: semID, 480 Desc: SemanticDesc{ 481 Bounds: a.bounds(), 482 Label: cnt.label, 483 Description: cnt.desc, 484 Class: cnt.class, 485 Gestures: cnt.gestures, 486 Selected: cnt.selected, 487 Disabled: cnt.disabled, 488 }, 489 areaIdx: areaIdx, 490 }) 491 } else { 492 nodes = q.appendSemanticChildren(nodes, a.firstChild) 493 } 494 return q.appendSemanticChildren(nodes, a.sibling) 495 } 496 497 func (q *pointerQueue) semanticIDFor(content semanticContent) SemanticID { 498 ids := q.semantic.contentIDs[content] 499 for i, id := range ids { 500 if !id.used { 501 ids[i].used = true 502 return id.id 503 } 504 } 505 // No prior assigned ID; allocate a new one. 506 q.semantic.lastID++ 507 id := semanticID{id: q.semantic.lastID, used: true} 508 if q.semantic.contentIDs == nil { 509 q.semantic.contentIDs = make(map[semanticContent][]semanticID) 510 } 511 q.semantic.contentIDs[content] = append(q.semantic.contentIDs[content], id) 512 return id.id 513 } 514 515 func (q *pointerQueue) ActionAt(pos f32.Point) (action system.Action, hasAction bool) { 516 q.hitTest(pos, func(n *hitNode) bool { 517 area := q.areas[n.area] 518 if area.action != 0 { 519 action = area.action 520 hasAction = true 521 return false 522 } 523 return true 524 }) 525 return action, hasAction 526 } 527 528 func (q *pointerQueue) SemanticAt(pos f32.Point) (semID SemanticID, hasSemID bool) { 529 q.assignSemIDs() 530 q.hitTest(pos, func(n *hitNode) bool { 531 area := q.areas[n.area] 532 if area.semantic.id != 0 { 533 semID = area.semantic.id 534 hasSemID = true 535 return false 536 } 537 return true 538 }) 539 return semID, hasSemID 540 } 541 542 // hitTest searches the hit tree for nodes matching pos. Any node matching pos will 543 // have the onNode func invoked on it to allow the caller to extract whatever information 544 // is necessary for further processing. onNode may return false to terminate the walk of 545 // the hit tree, or true to continue. Providing this algorithm in this generic way 546 // allows normal event routing and system action event routing to share the same traversal 547 // logic even though they are interested in different aspects of hit nodes. 548 func (q *pointerQueue) hitTest(pos f32.Point, onNode func(*hitNode) bool) pointer.Cursor { 549 // Track whether we're passing through hits. 550 pass := true 551 idx := len(q.hitTree) - 1 552 cursor := pointer.CursorDefault 553 for idx >= 0 { 554 n := &q.hitTree[idx] 555 hit, c := q.hit(n.area, pos) 556 if !hit { 557 idx-- 558 continue 559 } 560 if cursor == pointer.CursorDefault { 561 cursor = c 562 } 563 pass = pass && n.pass 564 if pass { 565 idx-- 566 } else { 567 idx = n.next 568 } 569 if !onNode(n) { 570 break 571 } 572 } 573 return cursor 574 } 575 576 func (q *pointerQueue) invTransform(areaIdx int, p f32.Point) f32.Point { 577 if areaIdx == -1 { 578 return p 579 } 580 return q.areas[areaIdx].trans.Invert().Transform(p) 581 } 582 583 func (q *pointerQueue) hit(areaIdx int, p f32.Point) (bool, pointer.Cursor) { 584 c := pointer.CursorDefault 585 for areaIdx != -1 { 586 a := &q.areas[areaIdx] 587 if c == pointer.CursorDefault { 588 c = a.cursor 589 } 590 p := a.trans.Invert().Transform(p) 591 if !a.area.Hit(p) { 592 return false, c 593 } 594 areaIdx = a.parent 595 } 596 return true, c 597 } 598 599 func (q *pointerQueue) reset() { 600 q.hitTree = q.hitTree[:0] 601 q.areas = q.areas[:0] 602 q.semantic.idsAssigned = false 603 for k, ids := range q.semantic.contentIDs { 604 for i := len(ids) - 1; i >= 0; i-- { 605 if !ids[i].used { 606 ids = append(ids[:i], ids[i+1:]...) 607 } else { 608 ids[i].used = false 609 } 610 } 611 if len(ids) > 0 { 612 q.semantic.contentIDs[k] = ids 613 } else { 614 delete(q.semantic.contentIDs, k) 615 } 616 } 617 } 618 619 func (q *pointerQueue) Frame(handlers map[event.Tag]*handler, state pointerState) (pointerState, []taggedEvent) { 620 for _, h := range handlers { 621 if h.pointer.areaPlusOne != 0 { 622 area := &q.areas[h.pointer.areaPlusOne-1] 623 if h.filter.pointer.kinds&(pointer.Press|pointer.Release) != 0 { 624 area.semantic.content.gestures |= ClickGesture 625 } 626 if h.filter.pointer.kinds&pointer.Scroll != 0 { 627 area.semantic.content.gestures |= ScrollGesture 628 } 629 area.semantic.valid = area.semantic.content.gestures != 0 630 } 631 } 632 var evts []taggedEvent 633 for i, p := range state.pointers { 634 changed := false 635 p, evts, state.cursor, changed = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, p.last) 636 if changed { 637 state.pointers = append([]pointerInfo{}, state.pointers...) 638 state.pointers[i] = p 639 } 640 } 641 return state, evts 642 } 643 644 func dropHandler(state pointerState, tag event.Tag) pointerState { 645 pointers := state.pointers 646 state.pointers = nil 647 for _, p := range pointers { 648 handlers := p.handlers 649 p.handlers = nil 650 for _, h := range handlers { 651 if h != tag { 652 p.handlers = append(p.handlers, h) 653 } 654 } 655 entered := p.entered 656 p.entered = nil 657 for _, h := range entered { 658 if h != tag { 659 p.entered = append(p.entered, h) 660 } 661 } 662 state.pointers = append(state.pointers, p) 663 } 664 return state 665 } 666 667 // pointerOf returns the pointerInfo index corresponding to the pointer in e. 668 func (s pointerState) pointerOf(e pointer.Event) (pointerState, int) { 669 for i, p := range s.pointers { 670 if p.id == e.PointerID { 671 return s, i 672 } 673 } 674 n := len(s.pointers) 675 s.pointers = append(s.pointers[:n:n], pointerInfo{id: e.PointerID}) 676 return s, len(s.pointers) - 1 677 } 678 679 // Deliver is like Push, but delivers an event to a particular area. 680 func (q *pointerQueue) Deliver(handlers map[event.Tag]*handler, areaIdx int, e pointer.Event) []taggedEvent { 681 scroll := e.Scroll 682 idx := len(q.hitTree) - 1 683 // Locate first potential receiver. 684 for idx != -1 { 685 n := &q.hitTree[idx] 686 if n.area == areaIdx { 687 break 688 } 689 idx-- 690 } 691 var evts []taggedEvent 692 for idx != -1 { 693 n := &q.hitTree[idx] 694 idx = n.next 695 h, ok := handlers[n.tag] 696 if !ok || !h.filter.pointer.Matches(e) { 697 continue 698 } 699 e := e 700 if e.Kind == pointer.Scroll { 701 if scroll == (f32.Point{}) { 702 break 703 } 704 scroll, e.Scroll = h.filter.pointer.clampScroll(scroll) 705 } 706 e.Position = q.invTransform(h.pointer.areaPlusOne-1, e.Position) 707 evts = append(evts, taggedEvent{tag: n.tag, event: e}) 708 if e.Kind != pointer.Scroll { 709 break 710 } 711 } 712 return evts 713 } 714 715 // SemanticArea returns the sematic content for area, and its parent area. 716 func (q *pointerQueue) SemanticArea(areaIdx int) (semanticContent, int) { 717 for areaIdx != -1 { 718 a := &q.areas[areaIdx] 719 areaIdx = a.parent 720 if !a.semantic.valid { 721 continue 722 } 723 return a.semantic.content, areaIdx 724 } 725 return semanticContent{}, -1 726 } 727 728 func (q *pointerQueue) Push(handlers map[event.Tag]*handler, state pointerState, e pointer.Event) (pointerState, []taggedEvent) { 729 var evts []taggedEvent 730 if e.Kind == pointer.Cancel { 731 for k := range handlers { 732 evts = append(evts, taggedEvent{ 733 event: pointer.Event{Kind: pointer.Cancel}, 734 tag: k, 735 }) 736 } 737 state.pointers = nil 738 return state, evts 739 } 740 state, pidx := state.pointerOf(e) 741 p := state.pointers[pidx] 742 743 switch e.Kind { 744 case pointer.Press: 745 p, evts, state.cursor, _ = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, e) 746 p.pressed = true 747 evts = q.deliverEvent(handlers, p, evts, e) 748 case pointer.Move: 749 if p.pressed { 750 e.Kind = pointer.Drag 751 } 752 p, evts, state.cursor, _ = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, e) 753 evts = q.deliverEvent(handlers, p, evts, e) 754 if p.pressed { 755 p, evts = q.deliverDragEvent(handlers, p, evts) 756 } 757 case pointer.Release: 758 evts = q.deliverEvent(handlers, p, evts, e) 759 p.pressed = false 760 p, evts, state.cursor, _ = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, e) 761 p, evts = q.deliverDropEvent(handlers, p, evts) 762 case pointer.Scroll: 763 p, evts, state.cursor, _ = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, e) 764 evts = q.deliverEvent(handlers, p, evts, e) 765 default: 766 panic("unsupported pointer event type") 767 } 768 769 p.last = e 770 771 if !p.pressed && len(p.entered) == 0 { 772 // No longer need to track pointer. 773 state.pointers = append(state.pointers[:pidx:pidx], state.pointers[pidx+1:]...) 774 } else { 775 state.pointers = append([]pointerInfo{}, state.pointers...) 776 state.pointers[pidx] = p 777 } 778 return state, evts 779 } 780 781 func (q *pointerQueue) deliverEvent(handlers map[event.Tag]*handler, p pointerInfo, evts []taggedEvent, e pointer.Event) []taggedEvent { 782 foremost := true 783 if p.pressed && len(p.handlers) == 1 { 784 e.Priority = pointer.Grabbed 785 foremost = false 786 } 787 scroll := e.Scroll 788 for _, k := range p.handlers { 789 h, ok := handlers[k] 790 if !ok { 791 continue 792 } 793 f := h.filter.pointer 794 if !f.Matches(e) { 795 continue 796 } 797 if e.Kind == pointer.Scroll { 798 if scroll == (f32.Point{}) { 799 return evts 800 } 801 scroll, e.Scroll = f.clampScroll(scroll) 802 } 803 e := e 804 if foremost { 805 foremost = false 806 e.Priority = pointer.Foremost 807 } 808 e.Position = q.invTransform(h.pointer.areaPlusOne-1, e.Position) 809 evts = append(evts, taggedEvent{event: e, tag: k}) 810 } 811 return evts 812 } 813 814 func (q *pointerQueue) deliverEnterLeaveEvents(handlers map[event.Tag]*handler, cursor pointer.Cursor, p pointerInfo, evts []taggedEvent, e pointer.Event) (pointerInfo, []taggedEvent, pointer.Cursor, bool) { 815 changed := false 816 var hits []event.Tag 817 if e.Source != pointer.Mouse && !p.pressed && e.Kind != pointer.Press { 818 // Consider non-mouse pointers leaving when they're released. 819 } else { 820 var transSrc *pointerFilter 821 if p.dataSource != nil { 822 transSrc = &handlers[p.dataSource].filter.pointer 823 } 824 cursor = q.hitTest(e.Position, func(n *hitNode) bool { 825 h, ok := handlers[n.tag] 826 if !ok { 827 return true 828 } 829 add := true 830 if p.pressed { 831 add = false 832 // Filter out non-participating handlers, 833 // except potential transfer targets when a transfer has been initiated. 834 if _, found := searchTag(p.handlers, n.tag); found { 835 add = true 836 } 837 if transSrc != nil { 838 if _, ok := firstMimeMatch(transSrc, &h.filter.pointer); ok { 839 add = true 840 } 841 } 842 } 843 if add { 844 hits = addHandler(hits, n.tag) 845 } 846 return true 847 }) 848 if !p.pressed { 849 changed = true 850 p.handlers = hits 851 } 852 } 853 // Deliver Leave events. 854 for _, k := range p.entered { 855 if _, found := searchTag(hits, k); found { 856 continue 857 } 858 h, ok := handlers[k] 859 if !ok { 860 continue 861 } 862 changed = true 863 e := e 864 e.Kind = pointer.Leave 865 866 if h.filter.pointer.Matches(e) { 867 e.Position = q.invTransform(h.pointer.areaPlusOne-1, e.Position) 868 evts = append(evts, taggedEvent{tag: k, event: e}) 869 } 870 } 871 // Deliver Enter events. 872 for _, k := range hits { 873 if _, found := searchTag(p.entered, k); found { 874 continue 875 } 876 h, ok := handlers[k] 877 if !ok { 878 continue 879 } 880 changed = true 881 e := e 882 e.Kind = pointer.Enter 883 884 if h.filter.pointer.Matches(e) { 885 e.Position = q.invTransform(h.pointer.areaPlusOne-1, e.Position) 886 evts = append(evts, taggedEvent{tag: k, event: e}) 887 } 888 } 889 p.entered = hits 890 return p, evts, cursor, changed 891 } 892 893 func (q *pointerQueue) deliverDragEvent(handlers map[event.Tag]*handler, p pointerInfo, evts []taggedEvent) (pointerInfo, []taggedEvent) { 894 if p.dataSource != nil { 895 return p, evts 896 } 897 // Identify the data source. 898 for _, k := range p.entered { 899 src := &handlers[k].filter.pointer 900 if len(src.sourceMimes) == 0 { 901 continue 902 } 903 // One data source handler per pointer. 904 p.dataSource = k 905 // Notify all potential targets. 906 for k, tgt := range handlers { 907 if _, ok := firstMimeMatch(src, &tgt.filter.pointer); ok { 908 evts = append(evts, taggedEvent{tag: k, event: transfer.InitiateEvent{}}) 909 } 910 } 911 break 912 } 913 return p, evts 914 } 915 916 func (q *pointerQueue) deliverDropEvent(handlers map[event.Tag]*handler, p pointerInfo, evts []taggedEvent) (pointerInfo, []taggedEvent) { 917 if p.dataSource == nil { 918 return p, evts 919 } 920 // Request data from the source. 921 src := &handlers[p.dataSource].filter.pointer 922 for _, k := range p.entered { 923 h := handlers[k] 924 if m, ok := firstMimeMatch(src, &h.filter.pointer); ok { 925 p.dataTarget = k 926 evts = append(evts, taggedEvent{tag: p.dataSource, event: transfer.RequestEvent{Type: m}}) 927 return p, evts 928 } 929 } 930 // No valid target found, abort. 931 return q.deliverTransferCancelEvent(handlers, p, evts) 932 } 933 934 func (q *pointerQueue) deliverTransferCancelEvent(handlers map[event.Tag]*handler, p pointerInfo, evts []taggedEvent) (pointerInfo, []taggedEvent) { 935 evts = append(evts, taggedEvent{tag: p.dataSource, event: transfer.CancelEvent{}}) 936 // Cancel all potential targets. 937 src := &handlers[p.dataSource].filter.pointer 938 for k, h := range handlers { 939 if _, ok := firstMimeMatch(src, &h.filter.pointer); ok { 940 evts = append(evts, taggedEvent{tag: k, event: transfer.CancelEvent{}}) 941 } 942 } 943 p.dataSource = nil 944 p.dataTarget = nil 945 return p, evts 946 } 947 948 // ClipFor clips r to the parents of area. 949 func (q *pointerQueue) ClipFor(area int, r image.Rectangle) image.Rectangle { 950 a := &q.areas[area] 951 parent := a.parent 952 for parent != -1 { 953 a := &q.areas[parent] 954 r = r.Intersect(a.bounds()) 955 parent = a.parent 956 } 957 return r 958 } 959 960 func searchTag(tags []event.Tag, tag event.Tag) (int, bool) { 961 for i, t := range tags { 962 if t == tag { 963 return i, true 964 } 965 } 966 return 0, false 967 } 968 969 // addHandler adds tag to the slice if not present. 970 func addHandler(tags []event.Tag, tag event.Tag) []event.Tag { 971 for _, t := range tags { 972 if t == tag { 973 return tags 974 } 975 } 976 return append(tags, tag) 977 } 978 979 // firstMimeMatch returns the first type match between src and tgt. 980 func firstMimeMatch(src, tgt *pointerFilter) (first string, matched bool) { 981 for _, m1 := range tgt.targetMimes { 982 for _, m2 := range src.sourceMimes { 983 if m1 == m2 { 984 return m1, true 985 } 986 } 987 } 988 return "", false 989 } 990 991 func (op *areaOp) Hit(pos f32.Point) bool { 992 pos = pos.Sub(f32internal.FPt(op.rect.Min)) 993 size := f32internal.FPt(op.rect.Size()) 994 switch op.kind { 995 case areaRect: 996 return 0 <= pos.X && pos.X < size.X && 997 0 <= pos.Y && pos.Y < size.Y 998 case areaEllipse: 999 rx := size.X / 2 1000 ry := size.Y / 2 1001 xh := pos.X - rx 1002 yk := pos.Y - ry 1003 // The ellipse function works in all cases because 1004 // 0/0 is not <= 1. 1005 return (xh*xh)/(rx*rx)+(yk*yk)/(ry*ry) <= 1 1006 default: 1007 panic("invalid area kind") 1008 } 1009 } 1010 1011 func (a *areaNode) bounds() image.Rectangle { 1012 return f32internal.Rectangle{ 1013 Min: a.trans.Transform(f32internal.FPt(a.area.rect.Min)), 1014 Max: a.trans.Transform(f32internal.FPt(a.area.rect.Max)), 1015 }.Round() 1016 }