gioui.org@v0.6.1-0.20240506124620-7a9ce51988ce/io/input/router.go (about) 1 // SPDX-License-Identifier: Unlicense OR MIT 2 3 package input 4 5 import ( 6 "image" 7 "io" 8 "strings" 9 "time" 10 11 "gioui.org/f32" 12 f32internal "gioui.org/internal/f32" 13 "gioui.org/internal/ops" 14 "gioui.org/io/clipboard" 15 "gioui.org/io/event" 16 "gioui.org/io/key" 17 "gioui.org/io/pointer" 18 "gioui.org/io/semantic" 19 "gioui.org/io/system" 20 "gioui.org/io/transfer" 21 "gioui.org/op" 22 ) 23 24 // Router tracks the [io/event.Tag] identifiers of user interface widgets 25 // and routes events to them. [Source] is its interface exposed to widgets. 26 type Router struct { 27 savedTrans []f32.Affine2D 28 transStack []f32.Affine2D 29 handlers map[event.Tag]*handler 30 pointer struct { 31 queue pointerQueue 32 collector pointerCollector 33 } 34 key struct { 35 queue keyQueue 36 // The following fields have the same purpose as the fields in 37 // type handler, but for key.Events. 38 filter keyFilter 39 nextFilter keyFilter 40 processedFilter keyFilter 41 scratchFilter keyFilter 42 } 43 cqueue clipboardQueue 44 // states is the list of pending state changes resulting from 45 // incoming events. The first element, if present, contains the state 46 // and events for the current frame. 47 changes []stateChange 48 reader ops.Reader 49 // InvalidateCmd summary. 50 wakeup bool 51 wakeupTime time.Time 52 // Changes queued for next call to Frame. 53 commands []Command 54 // transfers is the pending transfer.DataEvent.Open functions. 55 transfers []io.ReadCloser 56 // deferring is set if command execution and event delivery is deferred 57 // to the next frame. 58 deferring bool 59 // scratchFilters is for garbage-free construction of ephemeral filters. 60 scratchFilters []taggedFilter 61 } 62 63 // Source implements the interface between a Router and user interface widgets. 64 // The value Source is disabled. 65 type Source struct { 66 r *Router 67 } 68 69 // Command represents a request such as moving the focus, or initiating a clipboard read. 70 // Commands are queued by calling [Source.Queue]. 71 type Command interface { 72 ImplementsCommand() 73 } 74 75 // SemanticNode represents a node in the tree describing the components 76 // contained in a frame. 77 type SemanticNode struct { 78 ID SemanticID 79 ParentID SemanticID 80 Children []SemanticNode 81 Desc SemanticDesc 82 83 areaIdx int 84 } 85 86 // SemanticDesc provides a semantic description of a UI component. 87 type SemanticDesc struct { 88 Class semantic.ClassOp 89 Description string 90 Label string 91 Selected bool 92 Disabled bool 93 Gestures SemanticGestures 94 Bounds image.Rectangle 95 } 96 97 // SemanticGestures is a bit-set of supported gestures. 98 type SemanticGestures int 99 100 const ( 101 ClickGesture SemanticGestures = 1 << iota 102 ScrollGesture 103 ) 104 105 // SemanticID uniquely identifies a SemanticDescription. 106 // 107 // By convention, the zero value denotes the non-existent ID. 108 type SemanticID uint 109 110 // SystemEvent is a marker for events that have platform specific 111 // side-effects. SystemEvents are never matched by catch-all filters. 112 type SystemEvent struct { 113 Event event.Event 114 } 115 116 // handler contains the per-handler state tracked by a [Router]. 117 type handler struct { 118 // active tracks whether the handler was active in the current 119 // frame. Router deletes state belonging to inactive handlers during Frame. 120 active bool 121 pointer pointerHandler 122 key keyHandler 123 // filter the handler has asked for through event handling 124 // in the previous frame. It is used for routing events in the 125 // current frame. 126 filter filter 127 // prevFilter is the filter being built in the current frame. 128 nextFilter filter 129 // processedFilter is the filters that have exhausted available events. 130 processedFilter filter 131 } 132 133 // filter is the union of a set of [io/event.Filters]. 134 type filter struct { 135 pointer pointerFilter 136 focusable bool 137 } 138 139 // taggedFilter is a filter for a particular tag. 140 type taggedFilter struct { 141 tag event.Tag 142 filter filter 143 } 144 145 // stateChange represents the new state and outgoing events 146 // resulting from an incoming event. 147 type stateChange struct { 148 // event, if set, is the trigger for the change. 149 event event.Event 150 state inputState 151 events []taggedEvent 152 } 153 154 // inputState represent a immutable snapshot of the state required 155 // to route events. 156 type inputState struct { 157 clipboardState 158 keyState 159 pointerState 160 } 161 162 // taggedEvent represents an event and its target handler. 163 type taggedEvent struct { 164 event event.Event 165 tag event.Tag 166 } 167 168 // Source returns a Source backed by this Router. 169 func (q *Router) Source() Source { 170 return Source{r: q} 171 } 172 173 // Execute a command. 174 func (s Source) Execute(c Command) { 175 if !s.Enabled() { 176 return 177 } 178 s.r.execute(c) 179 } 180 181 // Enabled reports whether the source is enabled. Only enabled 182 // Sources deliver events and respond to commands. 183 func (s Source) Enabled() bool { 184 return s.r != nil 185 } 186 187 // Focused reports whether tag is focused, according to the most recent 188 // [key.FocusEvent] delivered. 189 func (s Source) Focused(tag event.Tag) bool { 190 if !s.Enabled() { 191 return false 192 } 193 return s.r.state().keyState.focus == tag 194 } 195 196 // Event returns the next event that matches at least one of filters. 197 func (s Source) Event(filters ...event.Filter) (event.Event, bool) { 198 if !s.Enabled() { 199 return nil, false 200 } 201 return s.r.Event(filters...) 202 } 203 204 func (q *Router) Event(filters ...event.Filter) (event.Event, bool) { 205 // Merge filters into scratch filters. 206 q.scratchFilters = q.scratchFilters[:0] 207 q.key.scratchFilter = q.key.scratchFilter[:0] 208 for _, f := range filters { 209 var t event.Tag 210 switch f := f.(type) { 211 case key.Filter: 212 q.key.scratchFilter = append(q.key.scratchFilter, f) 213 continue 214 case transfer.SourceFilter: 215 t = f.Target 216 case transfer.TargetFilter: 217 t = f.Target 218 case key.FocusFilter: 219 t = f.Target 220 case pointer.Filter: 221 t = f.Target 222 } 223 if t == nil { 224 continue 225 } 226 var filter *filter 227 for i := range q.scratchFilters { 228 s := &q.scratchFilters[i] 229 if s.tag == t { 230 filter = &s.filter 231 break 232 } 233 } 234 if filter == nil { 235 n := len(q.scratchFilters) 236 if n < cap(q.scratchFilters) { 237 // Re-use previously allocated filter. 238 q.scratchFilters = q.scratchFilters[:n+1] 239 tf := &q.scratchFilters[n] 240 tf.tag = t 241 filter = &tf.filter 242 filter.Reset() 243 } else { 244 q.scratchFilters = append(q.scratchFilters, taggedFilter{tag: t}) 245 filter = &q.scratchFilters[n].filter 246 } 247 } 248 filter.Add(f) 249 } 250 for _, tf := range q.scratchFilters { 251 h := q.stateFor(tf.tag) 252 h.filter.Merge(tf.filter) 253 h.nextFilter.Merge(tf.filter) 254 } 255 q.key.filter = append(q.key.filter, q.key.scratchFilter...) 256 q.key.nextFilter = append(q.key.nextFilter, q.key.scratchFilter...) 257 // Deliver reset event, if any. 258 for _, f := range filters { 259 switch f := f.(type) { 260 case key.FocusFilter: 261 if f.Target == nil { 262 break 263 } 264 h := q.stateFor(f.Target) 265 if reset, ok := h.key.ResetEvent(); ok { 266 return reset, true 267 } 268 case pointer.Filter: 269 if f.Target == nil { 270 break 271 } 272 h := q.stateFor(f.Target) 273 if reset, ok := h.pointer.ResetEvent(); ok && h.filter.pointer.Matches(reset) { 274 return reset, true 275 } 276 } 277 } 278 if !q.deferring { 279 for i := range q.changes { 280 change := &q.changes[i] 281 for j, evt := range change.events { 282 match := false 283 switch e := evt.event.(type) { 284 case key.Event: 285 match = q.key.scratchFilter.Matches(change.state.keyState.focus, e, false) 286 default: 287 for _, tf := range q.scratchFilters { 288 if evt.tag == tf.tag && tf.filter.Matches(evt.event) { 289 match = true 290 break 291 } 292 } 293 } 294 if match { 295 change.events = append(change.events[:j], change.events[j+1:]...) 296 // Fast forward state to last matched. 297 q.collapseState(i) 298 return evt.event, true 299 } 300 } 301 } 302 } 303 for _, tf := range q.scratchFilters { 304 h := q.stateFor(tf.tag) 305 h.processedFilter.Merge(tf.filter) 306 } 307 q.key.processedFilter = append(q.key.processedFilter, q.key.scratchFilter...) 308 return nil, false 309 } 310 311 // collapseState in the interval [1;idx] into q.changes[0]. 312 func (q *Router) collapseState(idx int) { 313 if idx == 0 { 314 return 315 } 316 first := &q.changes[0] 317 first.state = q.changes[idx].state 318 for i := 1; i <= idx; i++ { 319 first.events = append(first.events, q.changes[i].events...) 320 } 321 q.changes = append(q.changes[:1], q.changes[idx+1:]...) 322 } 323 324 // Frame replaces the declared handlers from the supplied 325 // operation list. The text input state, wakeup time and whether 326 // there are active profile handlers is also saved. 327 func (q *Router) Frame(frame *op.Ops) { 328 var remaining []event.Event 329 if n := len(q.changes); n > 0 { 330 if q.deferring { 331 // Collect events for replay. 332 for _, ch := range q.changes[1:] { 333 remaining = append(remaining, ch.event) 334 } 335 q.changes = append(q.changes[:0], stateChange{state: q.changes[0].state}) 336 } else { 337 // Collapse state. 338 state := q.changes[n-1].state 339 q.changes = append(q.changes[:0], stateChange{state: state}) 340 } 341 } 342 for _, rc := range q.transfers { 343 if rc != nil { 344 rc.Close() 345 } 346 } 347 q.transfers = nil 348 q.deferring = false 349 for _, h := range q.handlers { 350 h.filter, h.nextFilter = h.nextFilter, h.filter 351 h.nextFilter.Reset() 352 h.processedFilter.Reset() 353 h.pointer.Reset() 354 h.key.Reset() 355 } 356 q.key.filter, q.key.nextFilter = q.key.nextFilter, q.key.filter 357 q.key.nextFilter = q.key.nextFilter[:0] 358 var ops *ops.Ops 359 if frame != nil { 360 ops = &frame.Internal 361 } 362 q.reader.Reset(ops) 363 q.collect() 364 for k, h := range q.handlers { 365 if !h.active { 366 delete(q.handlers, k) 367 } else { 368 h.active = false 369 } 370 } 371 q.executeCommands() 372 q.Queue(remaining...) 373 st := q.lastState() 374 pst, evts := q.pointer.queue.Frame(q.handlers, st.pointerState) 375 st.pointerState = pst 376 st.keyState = q.key.queue.Frame(q.handlers, q.lastState().keyState) 377 q.changeState(nil, st, evts) 378 379 // Collapse state and events. 380 q.collapseState(len(q.changes) - 1) 381 } 382 383 // Queue events to be routed. 384 func (q *Router) Queue(events ...event.Event) { 385 for _, e := range events { 386 se, system := e.(SystemEvent) 387 if system { 388 e = se.Event 389 } 390 q.processEvent(e, system) 391 } 392 } 393 394 func (f *filter) Add(flt event.Filter) { 395 switch flt := flt.(type) { 396 case key.FocusFilter: 397 f.focusable = true 398 case pointer.Filter: 399 f.pointer.Add(flt) 400 case transfer.SourceFilter, transfer.TargetFilter: 401 f.pointer.Add(flt) 402 } 403 } 404 405 // Merge f2 into f. 406 func (f *filter) Merge(f2 filter) { 407 f.focusable = f.focusable || f2.focusable 408 f.pointer.Merge(f2.pointer) 409 } 410 411 func (f *filter) Matches(e event.Event) bool { 412 switch e.(type) { 413 case key.FocusEvent, key.SnippetEvent, key.EditEvent, key.SelectionEvent: 414 return f.focusable 415 default: 416 return f.pointer.Matches(e) 417 } 418 } 419 420 func (f *filter) Reset() { 421 *f = filter{ 422 pointer: pointerFilter{ 423 sourceMimes: f.pointer.sourceMimes[:0], 424 targetMimes: f.pointer.targetMimes[:0], 425 }, 426 } 427 } 428 429 func (q *Router) processEvent(e event.Event, system bool) { 430 state := q.lastState() 431 switch e := e.(type) { 432 case pointer.Event: 433 pstate, evts := q.pointer.queue.Push(q.handlers, state.pointerState, e) 434 state.pointerState = pstate 435 q.changeState(e, state, evts) 436 case key.Event: 437 var evts []taggedEvent 438 if q.key.filter.Matches(state.keyState.focus, e, system) { 439 evts = append(evts, taggedEvent{event: e}) 440 } 441 q.changeState(e, state, evts) 442 case key.SnippetEvent: 443 // Expand existing, overlapping snippet. 444 if r := state.content.Snippet.Range; rangeOverlaps(r, key.Range(e)) { 445 if e.Start > r.Start { 446 e.Start = r.Start 447 } 448 if e.End < r.End { 449 e.End = r.End 450 } 451 } 452 var evts []taggedEvent 453 if f := state.focus; f != nil { 454 evts = append(evts, taggedEvent{tag: f, event: e}) 455 } 456 q.changeState(e, state, evts) 457 case key.EditEvent, key.FocusEvent, key.SelectionEvent: 458 var evts []taggedEvent 459 if f := state.focus; f != nil { 460 evts = append(evts, taggedEvent{tag: f, event: e}) 461 } 462 q.changeState(e, state, evts) 463 case transfer.DataEvent: 464 cstate, evts := q.cqueue.Push(state.clipboardState, e) 465 state.clipboardState = cstate 466 q.changeState(e, state, evts) 467 default: 468 panic("unknown event type") 469 } 470 } 471 472 func (q *Router) execute(c Command) { 473 // The command can be executed immediately if event delivery is not frozen, and 474 // no event receiver has completed their event handling. 475 if !q.deferring { 476 ch := q.executeCommand(c) 477 immediate := true 478 for _, e := range ch.events { 479 h, ok := q.handlers[e.tag] 480 immediate = immediate && (!ok || !h.processedFilter.Matches(e.event)) 481 } 482 if immediate { 483 // Hold on to the remaining events for state replay. 484 var evts []event.Event 485 for _, ch := range q.changes { 486 if ch.event != nil { 487 evts = append(evts, ch.event) 488 } 489 } 490 if len(q.changes) > 1 { 491 q.changes = q.changes[:1] 492 } 493 q.changeState(nil, ch.state, ch.events) 494 q.Queue(evts...) 495 return 496 } 497 } 498 q.deferring = true 499 q.commands = append(q.commands, c) 500 } 501 502 func (q *Router) state() inputState { 503 if len(q.changes) > 0 { 504 return q.changes[0].state 505 } 506 return inputState{} 507 } 508 509 func (q *Router) lastState() inputState { 510 if n := len(q.changes); n > 0 { 511 return q.changes[n-1].state 512 } 513 return inputState{} 514 } 515 516 func (q *Router) executeCommands() { 517 for _, c := range q.commands { 518 ch := q.executeCommand(c) 519 q.changeState(nil, ch.state, ch.events) 520 } 521 q.commands = nil 522 } 523 524 // executeCommand the command and return the resulting state change along with the 525 // tag the state change depended on, if any. 526 func (q *Router) executeCommand(c Command) stateChange { 527 state := q.state() 528 var evts []taggedEvent 529 switch req := c.(type) { 530 case key.SelectionCmd: 531 state.keyState = q.key.queue.setSelection(state.keyState, req) 532 case key.FocusCmd: 533 state.keyState, evts = q.key.queue.Focus(q.handlers, state.keyState, req.Tag) 534 case key.SoftKeyboardCmd: 535 state.keyState = state.keyState.softKeyboard(req.Show) 536 case key.SnippetCmd: 537 state.keyState = q.key.queue.setSnippet(state.keyState, req) 538 case transfer.OfferCmd: 539 state.pointerState, evts = q.pointer.queue.offerData(q.handlers, state.pointerState, req) 540 case clipboard.WriteCmd: 541 q.cqueue.ProcessWriteClipboard(req) 542 case clipboard.ReadCmd: 543 state.clipboardState = q.cqueue.ProcessReadClipboard(state.clipboardState, req.Tag) 544 case pointer.GrabCmd: 545 state.pointerState, evts = q.pointer.queue.grab(state.pointerState, req) 546 case op.InvalidateCmd: 547 if !q.wakeup || req.At.Before(q.wakeupTime) { 548 q.wakeup = true 549 q.wakeupTime = req.At 550 } 551 } 552 return stateChange{state: state, events: evts} 553 } 554 555 func (q *Router) changeState(e event.Event, state inputState, evts []taggedEvent) { 556 // Wrap pointer.DataEvent.Open functions to detect them not being called. 557 for i := range evts { 558 e := &evts[i] 559 if de, ok := e.event.(transfer.DataEvent); ok { 560 transferIdx := len(q.transfers) 561 data := de.Open() 562 q.transfers = append(q.transfers, data) 563 de.Open = func() io.ReadCloser { 564 q.transfers[transferIdx] = nil 565 return data 566 } 567 e.event = de 568 } 569 } 570 // Initialize the first change to contain the current state 571 // and events that are bound for the current frame. 572 if len(q.changes) == 0 { 573 q.changes = append(q.changes, stateChange{}) 574 } 575 if e != nil && len(evts) > 0 { 576 // An event triggered events bound for user receivers. Add a state change to be 577 // able to redo the change in case of a command execution. 578 q.changes = append(q.changes, stateChange{event: e, state: state, events: evts}) 579 } else { 580 // Otherwise, merge with previous change. 581 prev := &q.changes[len(q.changes)-1] 582 prev.state = state 583 prev.events = append(prev.events, evts...) 584 } 585 } 586 587 func rangeOverlaps(r1, r2 key.Range) bool { 588 r1 = rangeNorm(r1) 589 r2 = rangeNorm(r2) 590 return r1.Start <= r2.Start && r2.Start < r1.End || 591 r1.Start <= r2.End && r2.End < r1.End 592 } 593 594 func rangeNorm(r key.Range) key.Range { 595 if r.End < r.Start { 596 r.End, r.Start = r.Start, r.End 597 } 598 return r 599 } 600 601 func (q *Router) MoveFocus(dir key.FocusDirection) { 602 state := q.lastState() 603 kstate, evts := q.key.queue.MoveFocus(q.handlers, state.keyState, dir) 604 state.keyState = kstate 605 q.changeState(nil, state, evts) 606 } 607 608 // RevealFocus scrolls the current focus (if any) into viewport 609 // if there are scrollable parent handlers. 610 func (q *Router) RevealFocus(viewport image.Rectangle) { 611 state := q.lastState() 612 focus := state.focus 613 if focus == nil { 614 return 615 } 616 kh := &q.handlers[focus].key 617 bounds := q.key.queue.BoundsFor(kh) 618 area := q.key.queue.AreaFor(kh) 619 viewport = q.pointer.queue.ClipFor(area, viewport) 620 621 topleft := bounds.Min.Sub(viewport.Min) 622 topleft = max(topleft, bounds.Max.Sub(viewport.Max)) 623 topleft = min(image.Pt(0, 0), topleft) 624 bottomright := bounds.Max.Sub(viewport.Max) 625 bottomright = min(bottomright, bounds.Min.Sub(viewport.Min)) 626 bottomright = max(image.Pt(0, 0), bottomright) 627 s := topleft 628 if s.X == 0 { 629 s.X = bottomright.X 630 } 631 if s.Y == 0 { 632 s.Y = bottomright.Y 633 } 634 q.ScrollFocus(s) 635 } 636 637 // ScrollFocus scrolls the focused widget, if any, by dist. 638 func (q *Router) ScrollFocus(dist image.Point) { 639 state := q.lastState() 640 focus := state.focus 641 if focus == nil { 642 return 643 } 644 kh := &q.handlers[focus].key 645 area := q.key.queue.AreaFor(kh) 646 q.changeState(nil, q.lastState(), q.pointer.queue.Deliver(q.handlers, area, pointer.Event{ 647 Kind: pointer.Scroll, 648 Source: pointer.Touch, 649 Scroll: f32internal.FPt(dist), 650 })) 651 } 652 653 func max(p1, p2 image.Point) image.Point { 654 m := p1 655 if p2.X > m.X { 656 m.X = p2.X 657 } 658 if p2.Y > m.Y { 659 m.Y = p2.Y 660 } 661 return m 662 } 663 664 func min(p1, p2 image.Point) image.Point { 665 m := p1 666 if p2.X < m.X { 667 m.X = p2.X 668 } 669 if p2.Y < m.Y { 670 m.Y = p2.Y 671 } 672 return m 673 } 674 675 func (q *Router) ActionAt(p f32.Point) (system.Action, bool) { 676 return q.pointer.queue.ActionAt(p) 677 } 678 679 func (q *Router) ClickFocus() { 680 focus := q.lastState().focus 681 if focus == nil { 682 return 683 } 684 kh := &q.handlers[focus].key 685 bounds := q.key.queue.BoundsFor(kh) 686 center := bounds.Max.Add(bounds.Min).Div(2) 687 e := pointer.Event{ 688 Position: f32.Pt(float32(center.X), float32(center.Y)), 689 Source: pointer.Touch, 690 } 691 area := q.key.queue.AreaFor(kh) 692 e.Kind = pointer.Press 693 state := q.lastState() 694 q.changeState(nil, state, q.pointer.queue.Deliver(q.handlers, area, e)) 695 e.Kind = pointer.Release 696 q.changeState(nil, state, q.pointer.queue.Deliver(q.handlers, area, e)) 697 } 698 699 // TextInputState returns the input state from the most recent 700 // call to Frame. 701 func (q *Router) TextInputState() TextInputState { 702 state := q.state() 703 kstate, s := state.InputState() 704 state.keyState = kstate 705 q.changeState(nil, state, nil) 706 return s 707 } 708 709 // TextInputHint returns the input mode from the most recent key.InputOp. 710 func (q *Router) TextInputHint() (key.InputHint, bool) { 711 return q.key.queue.InputHint(q.handlers, q.state().keyState) 712 } 713 714 // WriteClipboard returns the most recent content to be copied 715 // to the clipboard, if any. 716 func (q *Router) WriteClipboard() (mime string, content []byte, ok bool) { 717 return q.cqueue.WriteClipboard() 718 } 719 720 // ClipboardRequested reports if any new handler is waiting 721 // to read the clipboard. 722 func (q *Router) ClipboardRequested() bool { 723 return q.cqueue.ClipboardRequested(q.lastState().clipboardState) 724 } 725 726 // Cursor returns the last cursor set. 727 func (q *Router) Cursor() pointer.Cursor { 728 return q.state().cursor 729 } 730 731 // SemanticAt returns the first semantic description under pos, if any. 732 func (q *Router) SemanticAt(pos f32.Point) (SemanticID, bool) { 733 return q.pointer.queue.SemanticAt(pos) 734 } 735 736 // AppendSemantics appends the semantic tree to nodes, and returns the result. 737 // The root node is the first added. 738 func (q *Router) AppendSemantics(nodes []SemanticNode) []SemanticNode { 739 q.pointer.collector.q = &q.pointer.queue 740 q.pointer.collector.ensureRoot() 741 return q.pointer.queue.AppendSemantics(nodes) 742 } 743 744 // EditorState returns the editor state for the focused handler, or the 745 // zero value if there is none. 746 func (q *Router) EditorState() EditorState { 747 return q.key.queue.editorState(q.handlers, q.state().keyState) 748 } 749 750 func (q *Router) stateFor(tag event.Tag) *handler { 751 if tag == nil { 752 panic("internal error: nil tag") 753 } 754 s, ok := q.handlers[tag] 755 if !ok { 756 s = new(handler) 757 if q.handlers == nil { 758 q.handlers = make(map[event.Tag]*handler) 759 } 760 q.handlers[tag] = s 761 } 762 s.active = true 763 return s 764 } 765 766 func (q *Router) collect() { 767 q.transStack = q.transStack[:0] 768 pc := &q.pointer.collector 769 pc.q = &q.pointer.queue 770 pc.Reset() 771 kq := &q.key.queue 772 q.key.queue.Reset() 773 var t f32.Affine2D 774 for encOp, ok := q.reader.Decode(); ok; encOp, ok = q.reader.Decode() { 775 switch ops.OpType(encOp.Data[0]) { 776 case ops.TypeSave: 777 id := ops.DecodeSave(encOp.Data) 778 if extra := id - len(q.savedTrans) + 1; extra > 0 { 779 q.savedTrans = append(q.savedTrans, make([]f32.Affine2D, extra)...) 780 } 781 q.savedTrans[id] = t 782 case ops.TypeLoad: 783 id := ops.DecodeLoad(encOp.Data) 784 t = q.savedTrans[id] 785 pc.resetState() 786 pc.setTrans(t) 787 788 case ops.TypeClip: 789 var op ops.ClipOp 790 op.Decode(encOp.Data) 791 pc.clip(op) 792 case ops.TypePopClip: 793 pc.popArea() 794 case ops.TypeTransform: 795 t2, push := ops.DecodeTransform(encOp.Data) 796 if push { 797 q.transStack = append(q.transStack, t) 798 } 799 t = t.Mul(t2) 800 pc.setTrans(t) 801 case ops.TypePopTransform: 802 n := len(q.transStack) 803 t = q.transStack[n-1] 804 q.transStack = q.transStack[:n-1] 805 pc.setTrans(t) 806 807 case ops.TypeInput: 808 tag := encOp.Refs[0].(event.Tag) 809 s := q.stateFor(tag) 810 pc.inputOp(tag, &s.pointer) 811 a := pc.currentArea() 812 b := pc.currentAreaBounds() 813 if s.filter.focusable { 814 kq.inputOp(tag, &s.key, t, a, b) 815 } 816 817 // Pointer ops. 818 case ops.TypePass: 819 pc.pass() 820 case ops.TypePopPass: 821 pc.popPass() 822 case ops.TypeCursor: 823 name := pointer.Cursor(encOp.Data[1]) 824 pc.cursor(name) 825 case ops.TypeActionInput: 826 act := system.Action(encOp.Data[1]) 827 pc.actionInputOp(act) 828 case ops.TypeKeyInputHint: 829 op := key.InputHintOp{ 830 Tag: encOp.Refs[0].(event.Tag), 831 Hint: key.InputHint(encOp.Data[1]), 832 } 833 s := q.stateFor(op.Tag) 834 s.key.inputHint(op.Hint) 835 836 // Semantic ops. 837 case ops.TypeSemanticLabel: 838 lbl := *encOp.Refs[0].(*string) 839 pc.semanticLabel(lbl) 840 case ops.TypeSemanticDesc: 841 desc := *encOp.Refs[0].(*string) 842 pc.semanticDesc(desc) 843 case ops.TypeSemanticClass: 844 class := semantic.ClassOp(encOp.Data[1]) 845 pc.semanticClass(class) 846 case ops.TypeSemanticSelected: 847 if encOp.Data[1] != 0 { 848 pc.semanticSelected(true) 849 } else { 850 pc.semanticSelected(false) 851 } 852 case ops.TypeSemanticEnabled: 853 if encOp.Data[1] != 0 { 854 pc.semanticEnabled(true) 855 } else { 856 pc.semanticEnabled(false) 857 } 858 } 859 } 860 } 861 862 // WakeupTime returns the most recent time for doing another frame, 863 // as determined from the last call to Frame. 864 func (q *Router) WakeupTime() (time.Time, bool) { 865 t, w := q.wakeupTime, q.wakeup 866 q.wakeup = false 867 // Pending events always trigger wakeups. 868 if len(q.changes) > 1 || len(q.changes) == 1 && len(q.changes[0].events) > 0 { 869 t, w = time.Time{}, true 870 } 871 return t, w 872 } 873 874 func (s SemanticGestures) String() string { 875 var gestures []string 876 if s&ClickGesture != 0 { 877 gestures = append(gestures, "Click") 878 } 879 return strings.Join(gestures, ",") 880 } 881 882 func (SystemEvent) ImplementsEvent() {}