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