router.mx raw

   1  // SPDX-License-Identifier: Unlicense OR MIT
   2  
   3  // Full port of gioui.org/io/input (router.go, pointer.go, key.go, clipboard.go).
   4  // All sub-packages merged into package gio.
   5  
   6  package gio
   7  
   8  import (
   9  	"image"
  10  	"io"
  11  	"time"
  12  )
  13  
  14  // ---------------------------------------------------------------------------
  15  // Router
  16  // ---------------------------------------------------------------------------
  17  
  18  // Router tracks event.Tag identifiers and routes events to widget handlers.
  19  // Source is its interface exposed to widgets.
  20  type Router struct {
  21  	savedTrans []Affine2D
  22  	transStack []Affine2D
  23  	handlers   map[Tag]*routerHandler
  24  	pointer    struct {
  25  		queue     pointerQueue
  26  		collector pointerCollector
  27  	}
  28  	key struct {
  29  		queue         keyQueue
  30  		filter        keyFilterList
  31  		nextFilter    keyFilterList
  32  		scratchFilter keyFilterList
  33  	}
  34  	cqueue  clipboardQueue
  35  	changes []stateChange
  36  	reader  Reader
  37  	// InvalidateCmd summary.
  38  	wakeup     bool
  39  	wakeupTime time.Time
  40  	commands   []InputCommand
  41  	transfers  []io.ReadCloser
  42  	deferring  bool
  43  	// scratchFilters for garbage-free construction.
  44  	scratchFilters []taggedFilter
  45  }
  46  
  47  // Source implements the interface between a Router and widget handlers.
  48  // The zero value is disabled.
  49  type Source struct {
  50  	r        *Router
  51  	disabled bool
  52  }
  53  
  54  // routerHandler contains per-handler state tracked by a Router.
  55  type routerHandler struct {
  56  	active          bool
  57  	pointer         pointerHandler
  58  	key             keyHandler
  59  	filter          routerFilter
  60  	nextFilter      routerFilter
  61  	processedFilter routerFilter
  62  }
  63  
  64  // routerFilter is the union of a set of Filters.
  65  type routerFilter struct {
  66  	pointer   pointerFilter
  67  	focusable bool
  68  }
  69  
  70  // taggedFilter is a filter for a particular tag.
  71  type taggedFilter struct {
  72  	tag    Tag
  73  	filter routerFilter
  74  }
  75  
  76  // stateChange represents new state and outgoing events resulting from an incoming event.
  77  type stateChange struct {
  78  	event  Event
  79  	state  inputState
  80  	events []taggedEvent
  81  }
  82  
  83  // inputState is an immutable snapshot of routing state.
  84  type inputState struct {
  85  	clipboardState
  86  	keyState
  87  	pointerState
  88  }
  89  
  90  // taggedEvent represents an event and its target handler.
  91  type taggedEvent struct {
  92  	event Event
  93  	tag   Tag
  94  }
  95  
  96  // SystemEvent marks events with platform-specific side-effects.
  97  // SystemEvents are never matched by catch-all filters.
  98  type SystemEvent struct {
  99  	Event Event
 100  }
 101  
 102  func (SystemEvent) ImplementsEvent() {}
 103  
 104  // Source returns a Source backed by this Router.
 105  func (q *Router) Source() Source { return Source{r: q} }
 106  
 107  // Disabled returns a copy of this source that delivers no events.
 108  func (s Source) Disabled() Source { s2 := s; s2.disabled = true; return s2 }
 109  
 110  // Enabled reports whether the source is enabled.
 111  func (s Source) Enabled() bool { return s.r != nil && !s.disabled }
 112  
 113  // Focused reports whether tag is focused per the last delivered KeyFocusEvent.
 114  func (s Source) Focused(tag Tag) bool {
 115  	if !s.Enabled() {
 116  		return false
 117  	}
 118  	return s.r.state().keyState.focus == tag
 119  }
 120  
 121  // Execute queues a command for execution.
 122  func (s Source) Execute(c InputCommand) {
 123  	if !s.Enabled() {
 124  		return
 125  	}
 126  	s.r.execute(c)
 127  }
 128  
 129  // Event returns the next event matching at least one of filters.
 130  func (s Source) Event(filters ...Filter) (Event, bool) {
 131  	if !s.Enabled() {
 132  		return nil, false
 133  	}
 134  	return s.r.Event(filters...)
 135  }
 136  
 137  func (q *Router) Event(filters ...Filter) (Event, bool) {
 138  	q.scratchFilters = q.scratchFilters[:0]
 139  	q.key.scratchFilter = q.key.scratchFilter[:0]
 140  	for _, f := range filters {
 141  		var t Tag
 142  		switch f := f.(type) {
 143  		case KeyFilter:
 144  			q.key.scratchFilter = append(q.key.scratchFilter, f)
 145  			continue
 146  		case TransferSourceFilter:
 147  			t = f.Target
 148  		case TransferTargetFilter:
 149  			t = f.Target
 150  		case KeyFocusFilter:
 151  			t = f.Target
 152  		case PointerFilter:
 153  			t = f.Target
 154  		}
 155  		if t == nil {
 156  			continue
 157  		}
 158  		var flt *routerFilter
 159  		for i := range q.scratchFilters {
 160  			s := &q.scratchFilters[i]
 161  			if s.tag == t {
 162  				flt = &s.filter
 163  				break
 164  			}
 165  		}
 166  		if flt == nil {
 167  			n := len(q.scratchFilters)
 168  			if n < cap(q.scratchFilters) {
 169  				q.scratchFilters = q.scratchFilters[:n+1]
 170  				tf := &q.scratchFilters[n]
 171  				tf.tag = t
 172  				flt = &tf.filter
 173  				flt.Reset()
 174  			} else {
 175  				q.scratchFilters = append(q.scratchFilters, taggedFilter{tag: t})
 176  				flt = &q.scratchFilters[n].filter
 177  			}
 178  		}
 179  		flt.Add(f)
 180  	}
 181  	for _, tf := range q.scratchFilters {
 182  		h := q.stateFor(tf.tag)
 183  		h.filter.Merge(tf.filter)
 184  		h.nextFilter.Merge(tf.filter)
 185  	}
 186  	q.key.filter = append(q.key.filter, q.key.scratchFilter...)
 187  	q.key.nextFilter = append(q.key.nextFilter, q.key.scratchFilter...)
 188  	// Deliver reset events, if any.
 189  	for _, f := range filters {
 190  		switch f := f.(type) {
 191  		case KeyFocusFilter:
 192  			if f.Target == nil {
 193  				break
 194  			}
 195  			h := q.stateFor(f.Target)
 196  			if reset, ok := h.key.ResetEvent(); ok {
 197  				return reset, true
 198  			}
 199  		case PointerFilter:
 200  			if f.Target == nil {
 201  				break
 202  			}
 203  			h := q.stateFor(f.Target)
 204  			if reset, ok := h.pointer.ResetEvent(); ok && h.filter.pointer.Matches(reset) {
 205  				return reset, true
 206  			}
 207  		}
 208  	}
 209  	for i := range q.changes {
 210  		if q.deferring && i > 0 {
 211  			break
 212  		}
 213  		change := &q.changes[i]
 214  		for j, evt := range change.events {
 215  			match := false
 216  			switch e := evt.event.(type) {
 217  			case KeyEvent:
 218  				match = q.key.scratchFilter.Matches(change.state.keyState.focus, e, false)
 219  			default:
 220  				for _, tf := range q.scratchFilters {
 221  					if evt.tag == tf.tag && tf.filter.Matches(evt.event) {
 222  						match = true
 223  						break
 224  					}
 225  				}
 226  			}
 227  			if match {
 228  				change.events = deleteTaggedEvent(change.events, j, j+1)
 229  				q.collapseState(i)
 230  				return evt.event, true
 231  			}
 232  		}
 233  	}
 234  	for _, tf := range q.scratchFilters {
 235  		h := q.stateFor(tf.tag)
 236  		h.processedFilter.Merge(tf.filter)
 237  	}
 238  	return nil, false
 239  }
 240  
 241  func (q *Router) collapseState(idx int) {
 242  	if idx == 0 {
 243  		return
 244  	}
 245  	first := &q.changes[0]
 246  	first.state = q.changes[idx].state
 247  	for _, ch := range q.changes[1 : idx+1] {
 248  		first.events = append(first.events, ch.events...)
 249  	}
 250  	q.changes = append(q.changes[:1], q.changes[idx+1:]...)
 251  }
 252  
 253  // Frame completes the current frame, collects handlers from ops, and starts a new frame.
 254  func (q *Router) Frame(frame *OpList) {
 255  	var remaining []Event
 256  	if n := len(q.changes); n > 0 {
 257  		if q.deferring {
 258  			for _, ch := range q.changes[1:] {
 259  				remaining = append(remaining, ch.event)
 260  			}
 261  			q.changes = append(q.changes[:0], stateChange{state: q.changes[0].state})
 262  		} else {
 263  			state := q.changes[n-1].state
 264  			q.changes = append(q.changes[:0], stateChange{state: state})
 265  		}
 266  	}
 267  	for _, rc := range q.transfers {
 268  		if rc != nil {
 269  			rc.Close()
 270  		}
 271  	}
 272  	q.transfers = nil
 273  	q.deferring = false
 274  	for _, h := range q.handlers {
 275  		h.filter, h.nextFilter = h.nextFilter, h.filter
 276  		h.nextFilter.Reset()
 277  		h.processedFilter.Reset()
 278  		h.pointer.Reset()
 279  		h.key.Reset()
 280  	}
 281  	q.key.filter, q.key.nextFilter = q.key.nextFilter, q.key.filter
 282  	q.key.nextFilter = q.key.nextFilter[:0]
 283  	var ops *Ops
 284  	if frame != nil {
 285  		ops = &frame.Internal
 286  	}
 287  	q.reader.ResetReader(ops)
 288  	q.collect()
 289  	for k, h := range q.handlers {
 290  		if !h.active {
 291  			delete(q.handlers, k)
 292  		} else {
 293  			h.active = false
 294  		}
 295  	}
 296  	q.executeCommands()
 297  	q.Queue(remaining...)
 298  	st := q.lastState()
 299  	pst, evts := q.pointer.queue.Frame(q.handlers, st.pointerState)
 300  	st.pointerState = pst
 301  	st.keyState = q.key.queue.Frame(q.handlers, q.lastState().keyState)
 302  	q.changeState(nil, st, evts)
 303  	q.collapseState(len(q.changes) - 1)
 304  }
 305  
 306  // Queue events to be routed.
 307  func (q *Router) Queue(events ...Event) {
 308  	for _, e := range events {
 309  		se, system := e.(SystemEvent)
 310  		if system {
 311  			e = se.Event
 312  		}
 313  		q.processEvent(e, system)
 314  	}
 315  }
 316  
 317  // QueuePointer translates and queues AppPointerEvents.
 318  func (q *Router) QueuePointer(events []AppPointerEvent) {
 319  	for _, e := range events {
 320  		q.Queue(appPointerToPointerEvent(e))
 321  	}
 322  }
 323  
 324  // QueueKey translates and queues AppKeyEvents.
 325  func (q *Router) QueueKey(events []AppKeyEvent) {
 326  	for _, e := range events {
 327  		if ke, ok := appKeyToKeyEvent(e); ok {
 328  			q.Queue(ke)
 329  		}
 330  	}
 331  }
 332  
 333  func (f *routerFilter) Add(flt Filter) {
 334  	switch flt := flt.(type) {
 335  	case KeyFocusFilter:
 336  		f.focusable = true
 337  	case PointerFilter:
 338  		f.pointer.Add(flt)
 339  	case TransferSourceFilter, TransferTargetFilter:
 340  		f.pointer.AddTransfer(flt)
 341  	}
 342  }
 343  
 344  func (f *routerFilter) Merge(f2 routerFilter) {
 345  	f.focusable = f.focusable || f2.focusable
 346  	f.pointer.Merge(f2.pointer)
 347  }
 348  
 349  func (f *routerFilter) Matches(e Event) bool {
 350  	switch e.(type) {
 351  	case KeyFocusEvent, SnippetEvent, KeyEditEvent, SelectionEvent:
 352  		return f.focusable
 353  	default:
 354  		return f.pointer.Matches(e)
 355  	}
 356  }
 357  
 358  func (f *routerFilter) Reset() {
 359  	*f = routerFilter{
 360  		pointer: pointerFilter{
 361  			sourceMimes: f.pointer.sourceMimes[:0],
 362  			targetMimes: f.pointer.targetMimes[:0],
 363  		},
 364  	}
 365  }
 366  
 367  func (q *Router) processEvent(e Event, system bool) {
 368  	state := q.lastState()
 369  	switch e := e.(type) {
 370  	case PointerEvent:
 371  		pstate, evts := q.pointer.queue.Push(q.handlers, state.pointerState, e)
 372  		state.pointerState = pstate
 373  		q.changeState(e, state, evts)
 374  	case KeyEvent:
 375  		var evts []taggedEvent
 376  		if q.key.filter.Matches(state.keyState.focus, e, system) {
 377  			evts = append(evts, taggedEvent{event: e})
 378  		}
 379  		q.changeState(e, state, evts)
 380  	case SnippetEvent:
 381  		if r := state.content.Snippet.KeyRange; rangeOverlaps(r, KeyRange(e)) {
 382  			if e.Start > r.Start {
 383  				e.Start = r.Start
 384  			}
 385  			if e.End < r.End {
 386  				e.End = r.End
 387  			}
 388  		}
 389  		var evts []taggedEvent
 390  		if f := state.focus; f != nil {
 391  			evts = append(evts, taggedEvent{tag: f, event: e})
 392  		}
 393  		q.changeState(e, state, evts)
 394  	case KeyEditEvent, KeyFocusEvent, SelectionEvent:
 395  		var evts []taggedEvent
 396  		if f := state.focus; f != nil {
 397  			evts = append(evts, taggedEvent{tag: f, event: e})
 398  		}
 399  		q.changeState(e, state, evts)
 400  	case TransferDataEvent:
 401  		cstate, evts := q.cqueue.Push(state.clipboardState, e)
 402  		state.clipboardState = cstate
 403  		q.changeState(e, state, evts)
 404  	default:
 405  		// Unknown event type - ignore rather than panic for robustness.
 406  	}
 407  }
 408  
 409  func (q *Router) execute(c InputCommand) {
 410  	if !q.deferring {
 411  		ch := q.executeCommand(c)
 412  		immediate := true
 413  		for _, e := range ch.events {
 414  			h, ok := q.handlers[e.tag]
 415  			immediate = immediate && (!ok || !h.processedFilter.Matches(e.event))
 416  		}
 417  		if immediate {
 418  			var evts []Event
 419  			for _, ch := range q.changes {
 420  				if ch.event != nil {
 421  					evts = append(evts, ch.event)
 422  				}
 423  			}
 424  			if len(q.changes) > 1 {
 425  				q.changes = q.changes[:1]
 426  			}
 427  			q.changeState(nil, ch.state, ch.events)
 428  			q.Queue(evts...)
 429  			return
 430  		}
 431  	}
 432  	q.deferring = true
 433  	q.commands = append(q.commands, c)
 434  }
 435  
 436  func (q *Router) state() inputState {
 437  	if len(q.changes) > 0 {
 438  		return q.changes[0].state
 439  	}
 440  	return inputState{}
 441  }
 442  
 443  func (q *Router) lastState() inputState {
 444  	if n := len(q.changes); n > 0 {
 445  		return q.changes[n-1].state
 446  	}
 447  	return inputState{}
 448  }
 449  
 450  func (q *Router) executeCommands() {
 451  	for _, c := range q.commands {
 452  		ch := q.executeCommand(c)
 453  		q.changeState(nil, ch.state, ch.events)
 454  	}
 455  	q.commands = nil
 456  }
 457  
 458  func (q *Router) executeCommand(c InputCommand) stateChange {
 459  	state := q.state()
 460  	var evts []taggedEvent
 461  	switch req := c.(type) {
 462  	case SelectionCmd:
 463  		state.keyState = q.key.queue.setSelection(state.keyState, req)
 464  	case FocusCmd:
 465  		state.keyState, evts = q.key.queue.Focus(q.handlers, state.keyState, req.Tag)
 466  	case SoftKeyboardCmd:
 467  		state.keyState = state.keyState.softKeyboard(req.Show)
 468  	case SnippetCmd:
 469  		state.keyState = q.key.queue.setSnippet(state.keyState, req)
 470  	case TransferOfferCmd:
 471  		state.pointerState, evts = q.pointer.queue.offerData(q.handlers, state.pointerState, req)
 472  	case ClipboardWriteCmd:
 473  		q.cqueue.ProcessWriteClipboard(req)
 474  	case ClipboardReadCmd:
 475  		state.clipboardState = q.cqueue.ProcessReadClipboard(state.clipboardState, req.Tag)
 476  	case GrabCmd:
 477  		state.pointerState, evts = q.pointer.queue.grab(state.pointerState, req)
 478  	case InvalidateCmd:
 479  		if !q.wakeup || req.At.Before(q.wakeupTime) {
 480  			q.wakeup = true
 481  			q.wakeupTime = req.At
 482  		}
 483  	}
 484  	return stateChange{state: state, events: evts}
 485  }
 486  
 487  func (q *Router) changeState(e Event, state inputState, evts []taggedEvent) {
 488  	for i := range evts {
 489  		ev := &evts[i]
 490  		if de, ok := ev.event.(TransferDataEvent); ok {
 491  			transferIdx := len(q.transfers)
 492  			data := de.Open()
 493  			q.transfers = append(q.transfers, data)
 494  			de.Open = func() io.ReadCloser {
 495  				q.transfers[transferIdx] = nil
 496  				return data
 497  			}
 498  			ev.event = de
 499  		}
 500  	}
 501  	if len(q.changes) == 0 {
 502  		q.changes = append(q.changes, stateChange{})
 503  	}
 504  	if e != nil && len(evts) > 0 {
 505  		q.changes = append(q.changes, stateChange{event: e, state: state, events: evts})
 506  	} else {
 507  		prev := &q.changes[len(q.changes)-1]
 508  		prev.state = state
 509  		prev.events = append(prev.events, evts...)
 510  	}
 511  }
 512  
 513  func rangeOverlaps(r1, r2 KeyRange) bool {
 514  	r1 = rangeNorm(r1)
 515  	r2 = rangeNorm(r2)
 516  	return r1.Start <= r2.Start && r2.Start < r1.End ||
 517  		r1.Start <= r2.End && r2.End < r1.End
 518  }
 519  
 520  func rangeNorm(r KeyRange) KeyRange {
 521  	if r.End < r.Start {
 522  		r.End, r.Start = r.Start, r.End
 523  	}
 524  	return r
 525  }
 526  
 527  // MoveFocus attempts to move the focus in direction dir.
 528  func (q *Router) MoveFocus(dir FocusDirection) {
 529  	state := q.lastState()
 530  	kstate, evts := q.key.queue.MoveFocus(q.handlers, state.keyState, dir)
 531  	state.keyState = kstate
 532  	q.changeState(nil, state, evts)
 533  }
 534  
 535  // RevealFocus scrolls the current focus into viewport.
 536  func (q *Router) RevealFocus(viewport image.Rectangle) {
 537  	state := q.lastState()
 538  	focus := state.focus
 539  	if focus == nil {
 540  		return
 541  	}
 542  	kh := &q.handlers[focus].key
 543  	bounds := q.key.queue.BoundsFor(kh)
 544  	area := q.key.queue.AreaFor(kh)
 545  	viewport = q.pointer.queue.ClipFor(area, viewport)
 546  	topleft := bounds.Min.Sub(viewport.Min)
 547  	topleft = maxImgPt(topleft, bounds.Max.Sub(viewport.Max))
 548  	topleft = minImgPt(image.Pt(0, 0), topleft)
 549  	bottomright := bounds.Max.Sub(viewport.Max)
 550  	bottomright = minImgPt(bottomright, bounds.Min.Sub(viewport.Min))
 551  	bottomright = maxImgPt(image.Pt(0, 0), bottomright)
 552  	s := topleft
 553  	if s.X == 0 {
 554  		s.X = bottomright.X
 555  	}
 556  	if s.Y == 0 {
 557  		s.Y = bottomright.Y
 558  	}
 559  	q.ScrollFocus(s)
 560  }
 561  
 562  // ScrollFocus scrolls the focused widget by dist.
 563  func (q *Router) ScrollFocus(dist image.Point) {
 564  	state := q.lastState()
 565  	focus := state.focus
 566  	if focus == nil {
 567  		return
 568  	}
 569  	kh := &q.handlers[focus].key
 570  	area := q.key.queue.AreaFor(kh)
 571  	q.changeState(nil, q.lastState(), q.pointer.queue.Deliver(q.handlers, area, PointerEvent{
 572  		Kind:   PointerScroll,
 573  		Source: Touch,
 574  		Scroll: FPt(dist),
 575  	}))
 576  }
 577  
 578  // ActionAt returns the action at pos, if any.
 579  func (q *Router) ActionAt(p Point) (SystemAction, bool) {
 580  	return q.pointer.queue.ActionAt(p)
 581  }
 582  
 583  // ClickFocus synthesizes a click on the focused widget.
 584  func (q *Router) ClickFocus() {
 585  	focus := q.lastState().focus
 586  	if focus == nil {
 587  		return
 588  	}
 589  	kh := &q.handlers[focus].key
 590  	bounds := q.key.queue.BoundsFor(kh)
 591  	center := bounds.Max.Add(bounds.Min).Div(2)
 592  	e := PointerEvent{
 593  		Position: Pt(float32(center.X), float32(center.Y)),
 594  		Source:   Touch,
 595  	}
 596  	area := q.key.queue.AreaFor(kh)
 597  	e.Kind = PointerPress
 598  	state := q.lastState()
 599  	q.changeState(nil, state, q.pointer.queue.Deliver(q.handlers, area, e))
 600  	e.Kind = PointerRelease
 601  	q.changeState(nil, state, q.pointer.queue.Deliver(q.handlers, area, e))
 602  }
 603  
 604  // TextInputState returns the current text input state.
 605  func (q *Router) TextInputState() TextInputState {
 606  	state := q.state()
 607  	kstate, s := state.InputState()
 608  	state.keyState = kstate
 609  	q.changeState(nil, state, nil)
 610  	return s
 611  }
 612  
 613  // TextInputHint returns the current key input hint.
 614  func (q *Router) TextInputHint() (InputHint, bool) {
 615  	return q.key.queue.InputHint(q.handlers, q.state().keyState)
 616  }
 617  
 618  // WriteClipboard returns the most recent content to copy to clipboard.
 619  func (q *Router) WriteClipboard() (mime string, content []byte, ok bool) {
 620  	return q.cqueue.WriteClipboard()
 621  }
 622  
 623  // ClipboardRequested reports if any handler is waiting for clipboard contents.
 624  func (q *Router) ClipboardRequested() bool {
 625  	return q.cqueue.ClipboardRequested(q.lastState().clipboardState)
 626  }
 627  
 628  // Cursor returns the last cursor set.
 629  func (q *Router) Cursor() Cursor {
 630  	return q.state().cursor
 631  }
 632  
 633  // WakeupTime returns the most recent requested wakeup time.
 634  func (q *Router) WakeupTime() (time.Time, bool) {
 635  	t, w := q.wakeupTime, q.wakeup
 636  	q.wakeup = false
 637  	if len(q.changes) > 1 || len(q.changes) == 1 && len(q.changes[0].events) > 0 {
 638  		t, w = time.Time{}, true
 639  	}
 640  	return t, w
 641  }
 642  
 643  func (q *Router) stateFor(tag Tag) *routerHandler {
 644  	if tag == nil {
 645  		panic("internal error: nil tag")
 646  	}
 647  	s, ok := q.handlers[tag]
 648  	if !ok {
 649  		s = &routerHandler{}
 650  		if q.handlers == nil {
 651  			q.handlers = map[Tag]*routerHandler{}
 652  		}
 653  		q.handlers[tag] = s
 654  	}
 655  	s.active = true
 656  	return s
 657  }
 658  
 659  func (q *Router) collect() {
 660  	q.transStack = q.transStack[:0]
 661  	pc := &q.pointer.collector
 662  	pc.q = &q.pointer.queue
 663  	pc.Reset()
 664  	kq := &q.key.queue
 665  	q.key.queue.Reset()
 666  	t := AffineId()
 667  	for encOp, ok := q.reader.Decode(); ok; encOp, ok = q.reader.Decode() {
 668  		switch OpType(encOp.Data[0]) {
 669  		case TypeSave:
 670  			id := DecodeSave(encOp.Data)
 671  			for extra := id - len(q.savedTrans) + 1; extra > 0; extra-- {
 672  				q.savedTrans = append(q.savedTrans, AffineId())
 673  			}
 674  			q.savedTrans[id] = t
 675  		case TypeLoad:
 676  			id := DecodeLoad(encOp.Data)
 677  			t = q.savedTrans[id]
 678  			pc.resetState()
 679  			pc.setTrans(t)
 680  		case TypeClip:
 681  			var op DecodedClipOp
 682  			op.Decode(encOp.Data)
 683  			pc.clip(op)
 684  		case TypePopClip:
 685  			pc.popArea()
 686  		case TypeTransform:
 687  			t2, push := DecodeTransform(encOp.Data)
 688  			if push {
 689  				q.transStack = append(q.transStack, t)
 690  			}
 691  			t = t.Mul(t2)
 692  			pc.setTrans(t)
 693  		case TypePopTransform:
 694  			n := len(q.transStack)
 695  			t = q.transStack[n-1]
 696  			q.transStack = q.transStack[:n-1]
 697  			pc.setTrans(t)
 698  		case TypeInput:
 699  			tag := encOp.Refs[0].(Tag)
 700  			s := q.stateFor(tag)
 701  			pc.inputOp(tag, &s.pointer)
 702  			a := pc.currentArea()
 703  			b := pc.currentAreaBounds()
 704  			if s.filter.focusable {
 705  				kq.inputOp(tag, &s.key, t, a, b)
 706  			}
 707  		case TypePass:
 708  			pc.pass()
 709  		case TypePopPass:
 710  			pc.popPass()
 711  		case TypeCursor:
 712  			name := Cursor(encOp.Data[1])
 713  			pc.cursor(name)
 714  		case TypeActionInput:
 715  			act := SystemAction(encOp.Data[1])
 716  			pc.actionInputOp(act)
 717  		case TypeKeyInputHint:
 718  			op := KeyInputHintOp{
 719  				Tag:  encOp.Refs[0].(Tag),
 720  				Hint: InputHint(encOp.Data[1]),
 721  			}
 722  			s := q.stateFor(op.Tag)
 723  			s.key.inputHint(op.Hint)
 724  		case TypeSemanticLabel:
 725  			lbl := *encOp.Refs[0].(*string)
 726  			pc.semanticLabel(lbl)
 727  		case TypeSemanticDesc:
 728  			desc := *encOp.Refs[0].(*string)
 729  			pc.semanticDesc(desc)
 730  		case TypeSemanticClass:
 731  			class := SemanticClassOp(encOp.Data[1])
 732  			pc.semanticClass(class)
 733  		case TypeSemanticSelected:
 734  			pc.semanticSelected(encOp.Data[1] != 0)
 735  		case TypeSemanticEnabled:
 736  			pc.semanticEnabled(encOp.Data[1] != 0)
 737  		}
 738  	}
 739  }
 740  
 741  // SemanticID uniquely identifies a SemanticDesc.
 742  type SemanticID uint
 743  
 744  // SemanticGestures is a bit-set of supported gestures.
 745  type SemanticGestures int
 746  
 747  const (
 748  	ClickGesture  SemanticGestures = 1 << iota
 749  	ScrollGesture
 750  )
 751  
 752  // SemanticNode represents a node in the semantic tree.
 753  type SemanticNode struct {
 754  	ID       SemanticID
 755  	ParentID SemanticID
 756  	Children []SemanticNode
 757  	Desc     SemanticDesc
 758  	areaIdx  int
 759  }
 760  
 761  // SemanticDesc provides a semantic description of a UI component.
 762  type SemanticDesc struct {
 763  	Class       SemanticClassOp
 764  	Description string
 765  	Label       string
 766  	Selected    bool
 767  	Disabled    bool
 768  	Gestures    SemanticGestures
 769  	Bounds      image.Rectangle
 770  }
 771  
 772  // SemanticAt returns the first semantic description under pos, if any.
 773  func (q *Router) SemanticAt(pos Point) (SemanticID, bool) {
 774  	return q.pointer.queue.SemanticAt(pos)
 775  }
 776  
 777  // AppendSemantics appends the semantic tree to nodes and returns the result.
 778  func (q *Router) AppendSemantics(nodes []SemanticNode) []SemanticNode {
 779  	q.pointer.collector.q = &q.pointer.queue
 780  	q.pointer.collector.ensureRoot()
 781  	return q.pointer.queue.AppendSemantics(nodes)
 782  }
 783  
 784  // EditorState returns the editor state for the focused handler.
 785  func (q *Router) EditorState() EditorState {
 786  	return q.key.queue.editorState(q.handlers, q.state().keyState)
 787  }
 788  
 789  // EditorState represents the state of an editor.
 790  type EditorState struct {
 791  	Selection struct {
 792  		Transform Affine2D
 793  		KeyRange
 794  		Caret
 795  	}
 796  	Snippet Snippet
 797  }
 798  
 799  // TextInputState represents the text input open/close state.
 800  type TextInputState uint8
 801  
 802  const (
 803  	TextInputKeep  TextInputState = iota
 804  	TextInputClose
 805  	TextInputOpen
 806  )
 807  
 808  func maxImgPt(p1, p2 image.Point) image.Point {
 809  	if p2.X > p1.X {
 810  		p1.X = p2.X
 811  	}
 812  	if p2.Y > p1.Y {
 813  		p1.Y = p2.Y
 814  	}
 815  	return p1
 816  }
 817  
 818  func minImgPt(p1, p2 image.Point) image.Point {
 819  	if p2.X < p1.X {
 820  		p1.X = p2.X
 821  	}
 822  	if p2.Y < p1.Y {
 823  		p1.Y = p2.Y
 824  	}
 825  	return p1
 826  }
 827  
 828  // ---------------------------------------------------------------------------
 829  // Pointer queue (ported from pointer.go)
 830  // ---------------------------------------------------------------------------
 831  
 832  type pointerQueue struct {
 833  	hitTree []hitNode
 834  	areas   []areaNode
 835  
 836  	semantic struct {
 837  		idsAssigned bool
 838  		lastID      SemanticID
 839  		contentIDs  map[semanticContent][]semanticID
 840  	}
 841  }
 842  
 843  type hitNode struct {
 844  	next int
 845  	area int
 846  	tag  Tag
 847  	pass bool
 848  }
 849  
 850  type pointerState struct {
 851  	cursor   Cursor
 852  	pointers []pointerInfo
 853  }
 854  
 855  type pointerInfo struct {
 856  	id       PointerID
 857  	pressed  bool
 858  	handlers []Tag
 859  	last     PointerEvent
 860  	entered  []Tag
 861  	dataSource Tag
 862  	dataTarget Tag
 863  }
 864  
 865  type pointerHandler struct {
 866  	areaPlusOne int
 867  	setup       bool
 868  }
 869  
 870  type pointerFilter struct {
 871  	kinds       PointerKind
 872  	scrollX     ScrollRange
 873  	scrollY     ScrollRange
 874  	sourceMimes []string
 875  	targetMimes []string
 876  }
 877  
 878  type areaOp struct {
 879  	kind areaKind
 880  	rect image.Rectangle
 881  }
 882  
 883  type areaNode struct {
 884  	trans      Affine2D
 885  	area       areaOp
 886  	cursor     Cursor
 887  	parent     int
 888  	firstChild int
 889  	lastChild  int
 890  	sibling    int
 891  	semantic struct {
 892  		valid   bool
 893  		id      SemanticID
 894  		content semanticContent
 895  	}
 896  	action SystemAction
 897  }
 898  
 899  type areaKind uint8
 900  
 901  const (
 902  	areaRect    areaKind = iota
 903  	areaEllipse
 904  )
 905  
 906  type collectState struct {
 907  	t           Affine2D
 908  	nodePlusOne int
 909  	pass        int
 910  }
 911  
 912  type pointerCollector struct {
 913  	q         *pointerQueue
 914  	state     collectState
 915  	nodeStack []int
 916  }
 917  
 918  type semanticContent struct {
 919  	tag      Tag
 920  	label    string
 921  	desc     string
 922  	class    SemanticClassOp
 923  	gestures SemanticGestures
 924  	selected bool
 925  	disabled bool
 926  }
 927  
 928  type semanticID struct {
 929  	id   SemanticID
 930  	used bool
 931  }
 932  
 933  func (c *pointerCollector) resetState() {
 934  	c.state = collectState{t: AffineId()}
 935  	c.nodeStack = c.nodeStack[:0]
 936  	if len(c.q.hitTree) > 0 {
 937  		c.state.nodePlusOne = 0 + 1
 938  	}
 939  }
 940  
 941  func (c *pointerCollector) setTrans(t Affine2D) { c.state.t = t }
 942  
 943  func (c *pointerCollector) clip(op DecodedClipOp) {
 944  	kind := areaRect
 945  	if op.Shape == ShapeEllipse {
 946  		kind = areaEllipse
 947  	}
 948  	c.pushArea(kind, op.Bounds)
 949  }
 950  
 951  func (c *pointerCollector) pushArea(kind areaKind, bounds image.Rectangle) {
 952  	parentID := c.currentArea()
 953  	areaID := len(c.q.areas)
 954  	if parentID != -1 {
 955  		parent := &c.q.areas[parentID]
 956  		if parent.firstChild == -1 {
 957  			parent.firstChild = areaID
 958  		}
 959  		if siblingID := parent.lastChild; siblingID != -1 {
 960  			c.q.areas[siblingID].sibling = areaID
 961  		}
 962  		parent.lastChild = areaID
 963  	}
 964  	an := areaNode{
 965  		trans:      c.state.t,
 966  		area:       areaOp{kind: kind, rect: bounds},
 967  		parent:     parentID,
 968  		sibling:    -1,
 969  		firstChild: -1,
 970  		lastChild:  -1,
 971  	}
 972  	c.q.areas = append(c.q.areas, an)
 973  	c.nodeStack = append(c.nodeStack, c.state.nodePlusOne-1)
 974  	c.addHitNode(hitNode{area: areaID, pass: true})
 975  }
 976  
 977  func (c *pointerCollector) popArea() {
 978  	n := len(c.nodeStack)
 979  	c.state.nodePlusOne = c.nodeStack[n-1] + 1
 980  	c.nodeStack = c.nodeStack[:n-1]
 981  }
 982  
 983  func (c *pointerCollector) pass()    { c.state.pass++ }
 984  func (c *pointerCollector) popPass() { c.state.pass-- }
 985  
 986  func (c *pointerCollector) currentArea() int {
 987  	if i := c.state.nodePlusOne - 1; i != -1 {
 988  		return c.q.hitTree[i].area
 989  	}
 990  	return -1
 991  }
 992  
 993  func (c *pointerCollector) currentAreaBounds() image.Rectangle {
 994  	a := c.currentArea()
 995  	if a == -1 {
 996  		panic("no root area")
 997  	}
 998  	return c.q.areas[a].bounds()
 999  }
1000  
1001  func (c *pointerCollector) addHitNode(n hitNode) {
1002  	n.next = c.state.nodePlusOne - 1
1003  	c.q.hitTree = append(c.q.hitTree, n)
1004  	c.state.nodePlusOne = len(c.q.hitTree) - 1 + 1
1005  }
1006  
1007  func (c *pointerCollector) newHandler(tag Tag, state *pointerHandler) {
1008  	areaID := c.currentArea()
1009  	c.addHitNode(hitNode{area: areaID, tag: tag, pass: c.state.pass > 0})
1010  	state.areaPlusOne = areaID + 1
1011  }
1012  
1013  func (s *pointerHandler) Reset()    { s.areaPlusOne = 0 }
1014  func (s *pointerHandler) ResetEvent() (Event, bool) {
1015  	if s.setup {
1016  		return nil, false
1017  	}
1018  	s.setup = true
1019  	return PointerEvent{Kind: PointerCancel}, true
1020  }
1021  
1022  func (c *pointerCollector) actionInputOp(act SystemAction) {
1023  	areaID := c.currentArea()
1024  	c.q.areas[areaID].action = act
1025  }
1026  
1027  func (c *pointerCollector) inputOp(tag Tag, state *pointerHandler) {
1028  	areaID := c.currentArea()
1029  	c.q.areas[areaID].semantic.content.tag = tag
1030  	c.newHandler(tag, state)
1031  }
1032  
1033  func (c *pointerCollector) cursor(cur Cursor) {
1034  	areaID := c.currentArea()
1035  	c.q.areas[areaID].cursor = cur
1036  }
1037  
1038  func (c *pointerCollector) semanticLabel(lbl string) {
1039  	areaID := c.currentArea()
1040  	a := &c.q.areas[areaID]
1041  	a.semantic.valid = true
1042  	a.semantic.content.label = lbl
1043  }
1044  
1045  func (c *pointerCollector) semanticDesc(desc string) {
1046  	areaID := c.currentArea()
1047  	a := &c.q.areas[areaID]
1048  	a.semantic.valid = true
1049  	a.semantic.content.desc = desc
1050  }
1051  
1052  func (c *pointerCollector) semanticClass(class SemanticClassOp) {
1053  	areaID := c.currentArea()
1054  	a := &c.q.areas[areaID]
1055  	a.semantic.valid = true
1056  	a.semantic.content.class = class
1057  }
1058  
1059  func (c *pointerCollector) semanticSelected(selected bool) {
1060  	areaID := c.currentArea()
1061  	a := &c.q.areas[areaID]
1062  	a.semantic.valid = true
1063  	a.semantic.content.selected = selected
1064  }
1065  
1066  func (c *pointerCollector) semanticEnabled(enabled bool) {
1067  	areaID := c.currentArea()
1068  	a := &c.q.areas[areaID]
1069  	a.semantic.valid = true
1070  	a.semantic.content.disabled = !enabled
1071  }
1072  
1073  func (c *pointerCollector) Reset() {
1074  	c.q.reset()
1075  	c.resetState()
1076  	c.ensureRoot()
1077  }
1078  
1079  func (c *pointerCollector) ensureRoot() {
1080  	if len(c.q.areas) > 0 {
1081  		return
1082  	}
1083  	c.pushArea(areaRect, image.Rect(-1e6, -1e6, 1e6, 1e6))
1084  	c.q.areas[0].semantic.valid = true
1085  }
1086  
1087  func (q *pointerQueue) reset() {
1088  	q.hitTree = q.hitTree[:0]
1089  	q.areas = q.areas[:0]
1090  	q.semantic.idsAssigned = false
1091  	for k, ids := range q.semantic.contentIDs {
1092  		for i := len(ids) - 1; i >= 0; i-- {
1093  			if !ids[i].used {
1094  				ids = deleteSemanticID(ids, i, i+1)
1095  			} else {
1096  				ids[i].used = false
1097  			}
1098  		}
1099  		if len(ids) > 0 {
1100  			q.semantic.contentIDs[k] = ids
1101  		} else {
1102  			delete(q.semantic.contentIDs, k)
1103  		}
1104  	}
1105  }
1106  
1107  func (q *pointerQueue) assignSemIDs() {
1108  	if q.semantic.idsAssigned {
1109  		return
1110  	}
1111  	q.semantic.idsAssigned = true
1112  	for i, a := range q.areas {
1113  		if a.semantic.valid {
1114  			q.areas[i].semantic.id = q.semanticIDFor(a.semantic.content)
1115  		}
1116  	}
1117  }
1118  
1119  func (q *pointerQueue) AppendSemantics(nodes []SemanticNode) []SemanticNode {
1120  	q.assignSemIDs()
1121  	nodes = q.appendSemanticChildren(nodes, 0)
1122  	nodes = q.appendSemanticArea(nodes, 0, 0)
1123  	return nodes
1124  }
1125  
1126  func (q *pointerQueue) appendSemanticArea(nodes []SemanticNode, parentID SemanticID, nodeIdx int) []SemanticNode {
1127  	areaIdx := nodes[nodeIdx].areaIdx
1128  	a := q.areas[areaIdx]
1129  	childStart := len(nodes)
1130  	nodes = q.appendSemanticChildren(nodes, a.firstChild)
1131  	childEnd := len(nodes)
1132  	for i := childStart; i < childEnd; i++ {
1133  		nodes = q.appendSemanticArea(nodes, a.semantic.id, i)
1134  	}
1135  	n := &nodes[nodeIdx]
1136  	n.ParentID = parentID
1137  	n.Children = nodes[childStart:childEnd]
1138  	return nodes
1139  }
1140  
1141  func (q *pointerQueue) appendSemanticChildren(nodes []SemanticNode, areaIdx int) []SemanticNode {
1142  	if areaIdx == -1 {
1143  		return nodes
1144  	}
1145  	a := q.areas[areaIdx]
1146  	if semID := a.semantic.id; semID != 0 {
1147  		cnt := a.semantic.content
1148  		nodes = append(nodes, SemanticNode{
1149  			ID: semID,
1150  			Desc: SemanticDesc{
1151  				Bounds:      a.bounds(),
1152  				Label:       cnt.label,
1153  				Description: cnt.desc,
1154  				Class:       cnt.class,
1155  				Gestures:    cnt.gestures,
1156  				Selected:    cnt.selected,
1157  				Disabled:    cnt.disabled,
1158  			},
1159  			areaIdx: areaIdx,
1160  		})
1161  	} else {
1162  		nodes = q.appendSemanticChildren(nodes, a.firstChild)
1163  	}
1164  	return q.appendSemanticChildren(nodes, a.sibling)
1165  }
1166  
1167  func (q *pointerQueue) semanticIDFor(content semanticContent) SemanticID {
1168  	ids := q.semantic.contentIDs[content]
1169  	for i, id := range ids {
1170  		if !id.used {
1171  			ids[i].used = true
1172  			return id.id
1173  		}
1174  	}
1175  	q.semantic.lastID++
1176  	id := semanticID{id: q.semantic.lastID, used: true}
1177  	if q.semantic.contentIDs == nil {
1178  		q.semantic.contentIDs = map[semanticContent][]semanticID{}
1179  	}
1180  	q.semantic.contentIDs[content] = append(q.semantic.contentIDs[content], id)
1181  	return id.id
1182  }
1183  
1184  func (q *pointerQueue) ActionAt(pos Point) (action SystemAction, hasAction bool) {
1185  	q.hitTest(pos, func(n *hitNode) bool {
1186  		area := q.areas[n.area]
1187  		if area.action != 0 {
1188  			action = area.action
1189  			hasAction = true
1190  			return false
1191  		}
1192  		return true
1193  	})
1194  	return action, hasAction
1195  }
1196  
1197  func (q *pointerQueue) SemanticAt(pos Point) (semID SemanticID, hasSemID bool) {
1198  	q.assignSemIDs()
1199  	q.hitTest(pos, func(n *hitNode) bool {
1200  		area := q.areas[n.area]
1201  		if area.semantic.id != 0 {
1202  			semID = area.semantic.id
1203  			hasSemID = true
1204  			return false
1205  		}
1206  		return true
1207  	})
1208  	return semID, hasSemID
1209  }
1210  
1211  func (q *pointerQueue) hitTest(pos Point, onNode func(*hitNode) bool) Cursor {
1212  	pass := true
1213  	idx := len(q.hitTree) - 1
1214  	cursor := CursorDefault
1215  	for idx >= 0 {
1216  		n := &q.hitTree[idx]
1217  		hit, c := q.hit(n.area, pos)
1218  		if !hit {
1219  			idx--
1220  			continue
1221  		}
1222  		if cursor == CursorDefault {
1223  			cursor = c
1224  		}
1225  		pass = pass && n.pass
1226  		if pass {
1227  			idx--
1228  		} else {
1229  			idx = n.next
1230  		}
1231  		if !onNode(n) {
1232  			break
1233  		}
1234  	}
1235  	return cursor
1236  }
1237  
1238  func (q *pointerQueue) invTransform(areaIdx int, p Point) Point {
1239  	if areaIdx == -1 {
1240  		return p
1241  	}
1242  	return q.areas[areaIdx].trans.Invert().Transform(p)
1243  }
1244  
1245  func (q *pointerQueue) hit(areaIdx int, p Point) (bool, Cursor) {
1246  	c := CursorDefault
1247  	for areaIdx != -1 {
1248  		a := &q.areas[areaIdx]
1249  		if c == CursorDefault {
1250  			c = a.cursor
1251  		}
1252  		tp := a.trans.Invert().Transform(p)
1253  		if !a.area.Hit(tp) {
1254  			return false, c
1255  		}
1256  		areaIdx = a.parent
1257  	}
1258  	return true, c
1259  }
1260  
1261  func (q *pointerQueue) grab(state pointerState, req GrabCmd) (pointerState, []taggedEvent) {
1262  	var evts []taggedEvent
1263  	for _, p := range state.pointers {
1264  		if !p.pressed || p.id != req.ID {
1265  			continue
1266  		}
1267  		found := containsTag(p.handlers, req.Tag)
1268  		if !found {
1269  			continue
1270  		}
1271  		for i := len(p.handlers) - 1; i >= 0; i-- {
1272  			if tag := p.handlers[i]; tag != req.Tag {
1273  				evts = append(evts, taggedEvent{
1274  					tag:   tag,
1275  					event: PointerEvent{Kind: PointerCancel},
1276  				})
1277  				state = dropHandler(state, tag)
1278  			}
1279  		}
1280  		break
1281  	}
1282  	return state, evts
1283  }
1284  
1285  func (q *pointerQueue) offerData(handlers map[Tag]*routerHandler, state pointerState, req TransferOfferCmd) (pointerState, []taggedEvent) {
1286  	var evts []taggedEvent
1287  	for i, p := range state.pointers {
1288  		if p.dataSource != req.Tag {
1289  			continue
1290  		}
1291  		if p.dataTarget != nil {
1292  			evts = append(evts, taggedEvent{tag: p.dataTarget, event: TransferDataEvent{
1293  				Type: req.Type,
1294  				Open: func() io.ReadCloser { return req.Data },
1295  			}})
1296  		}
1297  		state.pointers = clonePointers(state.pointers)
1298  		state.pointers[i], evts = q.deliverTransferCancelEvent(handlers, p, evts)
1299  		break
1300  	}
1301  	return state, evts
1302  }
1303  
1304  func (q *pointerQueue) Frame(handlers map[Tag]*routerHandler, state pointerState) (pointerState, []taggedEvent) {
1305  	for _, h := range handlers {
1306  		if h.pointer.areaPlusOne != 0 {
1307  			area := &q.areas[h.pointer.areaPlusOne-1]
1308  			if h.filter.pointer.kinds&(PointerPress|PointerRelease) != 0 {
1309  				area.semantic.content.gestures |= ClickGesture
1310  			}
1311  			if h.filter.pointer.kinds&PointerScroll != 0 {
1312  				area.semantic.content.gestures |= ScrollGesture
1313  			}
1314  			area.semantic.valid = area.semantic.content.gestures != 0
1315  		}
1316  	}
1317  	var evts []taggedEvent
1318  	for i, p := range state.pointers {
1319  		changed := false
1320  		p, evts, state.cursor, changed = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, p.last)
1321  		if changed {
1322  			state.pointers = clonePointers(state.pointers)
1323  			state.pointers[i] = p
1324  		}
1325  	}
1326  	return state, evts
1327  }
1328  
1329  func dropHandler(state pointerState, tag Tag) pointerState {
1330  	pointers := state.pointers
1331  	state.pointers = nil
1332  	for _, p := range pointers {
1333  		handlers := p.handlers
1334  		p.handlers = nil
1335  		for _, h := range handlers {
1336  			if h != tag {
1337  				p.handlers = append(p.handlers, h)
1338  			}
1339  		}
1340  		entered := p.entered
1341  		p.entered = nil
1342  		for _, h := range entered {
1343  			if h != tag {
1344  				p.entered = append(p.entered, h)
1345  			}
1346  		}
1347  		state.pointers = append(state.pointers, p)
1348  	}
1349  	return state
1350  }
1351  
1352  func (s pointerState) pointerOf(e PointerEvent) (pointerState, int) {
1353  	for i, p := range s.pointers {
1354  		if p.id == e.PointerID {
1355  			return s, i
1356  		}
1357  	}
1358  	n := len(s.pointers)
1359  	s.pointers = append(s.pointers[:n:n], pointerInfo{id: e.PointerID})
1360  	return s, len(s.pointers) - 1
1361  }
1362  
1363  func (q *pointerQueue) Deliver(handlers map[Tag]*routerHandler, areaIdx int, e PointerEvent) []taggedEvent {
1364  	scroll := e.Scroll
1365  	idx := len(q.hitTree) - 1
1366  	for idx != -1 {
1367  		n := &q.hitTree[idx]
1368  		if n.area == areaIdx {
1369  			break
1370  		}
1371  		idx--
1372  	}
1373  	var evts []taggedEvent
1374  	for idx != -1 {
1375  		n := &q.hitTree[idx]
1376  		idx = n.next
1377  		h, ok := handlers[n.tag]
1378  		if !ok || !h.filter.pointer.Matches(e) {
1379  			continue
1380  		}
1381  		e := e
1382  		if e.Kind == PointerScroll {
1383  			if scroll == (Point{}) {
1384  				break
1385  			}
1386  			scroll, e.Scroll = h.filter.pointer.clampScroll(scroll)
1387  		}
1388  		e.Position = q.invTransform(h.pointer.areaPlusOne-1, e.Position)
1389  		evts = append(evts, taggedEvent{tag: n.tag, event: e})
1390  		if e.Kind != PointerScroll {
1391  			break
1392  		}
1393  	}
1394  	return evts
1395  }
1396  
1397  func (q *pointerQueue) ClipFor(area int, r image.Rectangle) image.Rectangle {
1398  	a := &q.areas[area]
1399  	parent := a.parent
1400  	for parent != -1 {
1401  		a := &q.areas[parent]
1402  		r = r.Intersect(a.bounds())
1403  		parent = a.parent
1404  	}
1405  	return r
1406  }
1407  
1408  func (q *pointerQueue) Push(handlers map[Tag]*routerHandler, state pointerState, e PointerEvent) (pointerState, []taggedEvent) {
1409  	var evts []taggedEvent
1410  	if e.Kind == PointerCancel {
1411  		for k := range handlers {
1412  			evts = append(evts, taggedEvent{
1413  				event: PointerEvent{Kind: PointerCancel},
1414  				tag:   k,
1415  			})
1416  		}
1417  		state.pointers = nil
1418  		return state, evts
1419  	}
1420  	state, pidx := state.pointerOf(e)
1421  	p := state.pointers[pidx]
1422  
1423  	switch e.Kind {
1424  	case PointerPress:
1425  		p, evts, state.cursor, _ = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, e)
1426  		p.pressed = true
1427  		evts = q.deliverEvent(handlers, p, evts, e)
1428  	case PointerMove:
1429  		if p.pressed {
1430  			e.Kind = PointerDrag
1431  		}
1432  		p, evts, state.cursor, _ = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, e)
1433  		evts = q.deliverEvent(handlers, p, evts, e)
1434  		if p.pressed {
1435  			p, evts = q.deliverDragEvent(handlers, p, evts)
1436  		}
1437  	case PointerRelease:
1438  		evts = q.deliverEvent(handlers, p, evts, e)
1439  		p.pressed = false
1440  		p, evts, state.cursor, _ = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, e)
1441  		p, evts = q.deliverDropEvent(handlers, p, evts)
1442  	case PointerScroll:
1443  		p, evts, state.cursor, _ = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, e)
1444  		evts = q.deliverEvent(handlers, p, evts, e)
1445  	}
1446  
1447  	p.last = e
1448  	if !p.pressed && len(p.entered) == 0 {
1449  		// concat equivalent: remove pidx
1450  		state.pointers = concatPointers(state.pointers[:pidx:pidx], state.pointers[pidx+1:])
1451  	} else {
1452  		state.pointers = clonePointers(state.pointers)
1453  		state.pointers[pidx] = p
1454  	}
1455  	return state, evts
1456  }
1457  
1458  func (q *pointerQueue) deliverEvent(handlers map[Tag]*routerHandler, p pointerInfo, evts []taggedEvent, e PointerEvent) []taggedEvent {
1459  	if p.pressed && len(p.handlers) == 1 {
1460  		e.Priority = Grabbed
1461  	}
1462  	scroll := e.Scroll
1463  	for _, k := range p.handlers {
1464  		h, ok := handlers[k]
1465  		if !ok {
1466  			continue
1467  		}
1468  		f := h.filter.pointer
1469  		if !f.Matches(e) {
1470  			continue
1471  		}
1472  		if e.Kind == PointerScroll {
1473  			if scroll == (Point{}) {
1474  				return evts
1475  			}
1476  			scroll, e.Scroll = f.clampScroll(scroll)
1477  		}
1478  		ev := e
1479  		ev.Position = q.invTransform(h.pointer.areaPlusOne-1, e.Position)
1480  		evts = append(evts, taggedEvent{event: ev, tag: k})
1481  	}
1482  	return evts
1483  }
1484  
1485  func (q *pointerQueue) deliverEnterLeaveEvents(handlers map[Tag]*routerHandler, cursor Cursor, p pointerInfo, evts []taggedEvent, e PointerEvent) (pointerInfo, []taggedEvent, Cursor, bool) {
1486  	changed := false
1487  	var hits []Tag
1488  	if e.Source != Mouse && !p.pressed && e.Kind != PointerPress {
1489  		// Non-mouse pointers leave when released.
1490  	} else {
1491  		var transSrc *pointerFilter
1492  		if p.dataSource != nil {
1493  			transSrc = &handlers[p.dataSource].filter.pointer
1494  		}
1495  		cursor = q.hitTest(e.Position, func(n *hitNode) bool {
1496  			h, ok := handlers[n.tag]
1497  			if !ok {
1498  				return true
1499  			}
1500  			add := true
1501  			if p.pressed {
1502  				add = false
1503  				if _, found := searchTag(p.handlers, n.tag); found {
1504  					add = true
1505  				}
1506  				if transSrc != nil {
1507  					if _, ok := firstMimeMatch(transSrc, &h.filter.pointer); ok {
1508  						add = true
1509  					}
1510  				}
1511  			}
1512  			if add {
1513  				hits = addTagToList(hits, n.tag)
1514  			}
1515  			return true
1516  		})
1517  		if !p.pressed {
1518  			changed = true
1519  			p.handlers = hits
1520  		}
1521  	}
1522  	// Deliver Leave events.
1523  	for _, k := range p.entered {
1524  		if _, found := searchTag(hits, k); found {
1525  			continue
1526  		}
1527  		h, ok := handlers[k]
1528  		if !ok {
1529  			continue
1530  		}
1531  		changed = true
1532  		ev := e
1533  		ev.Kind = PointerLeave
1534  		if h.filter.pointer.Matches(ev) {
1535  			ev.Position = q.invTransform(h.pointer.areaPlusOne-1, ev.Position)
1536  			evts = append(evts, taggedEvent{tag: k, event: ev})
1537  		}
1538  	}
1539  	// Deliver Enter events.
1540  	for _, k := range hits {
1541  		if _, found := searchTag(p.entered, k); found {
1542  			continue
1543  		}
1544  		h, ok := handlers[k]
1545  		if !ok {
1546  			continue
1547  		}
1548  		changed = true
1549  		ev := e
1550  		ev.Kind = PointerEnter
1551  		if h.filter.pointer.Matches(ev) {
1552  			ev.Position = q.invTransform(h.pointer.areaPlusOne-1, ev.Position)
1553  			evts = append(evts, taggedEvent{tag: k, event: ev})
1554  		}
1555  	}
1556  	p.entered = hits
1557  	return p, evts, cursor, changed
1558  }
1559  
1560  func (q *pointerQueue) deliverDragEvent(handlers map[Tag]*routerHandler, p pointerInfo, evts []taggedEvent) (pointerInfo, []taggedEvent) {
1561  	if p.dataSource != nil {
1562  		return p, evts
1563  	}
1564  	for _, k := range p.entered {
1565  		src := &handlers[k].filter.pointer
1566  		if len(src.sourceMimes) == 0 {
1567  			continue
1568  		}
1569  		p.dataSource = k
1570  		for k, tgt := range handlers {
1571  			if _, ok := firstMimeMatch(src, &tgt.filter.pointer); ok {
1572  				evts = append(evts, taggedEvent{tag: k, event: TransferInitiateEvent{}})
1573  			}
1574  		}
1575  		break
1576  	}
1577  	return p, evts
1578  }
1579  
1580  func (q *pointerQueue) deliverDropEvent(handlers map[Tag]*routerHandler, p pointerInfo, evts []taggedEvent) (pointerInfo, []taggedEvent) {
1581  	if p.dataSource == nil {
1582  		return p, evts
1583  	}
1584  	src := &handlers[p.dataSource].filter.pointer
1585  	for _, k := range p.entered {
1586  		h := handlers[k]
1587  		if m, ok := firstMimeMatch(src, &h.filter.pointer); ok {
1588  			p.dataTarget = k
1589  			evts = append(evts, taggedEvent{tag: p.dataSource, event: TransferRequestEvent{Type: m}})
1590  			return p, evts
1591  		}
1592  	}
1593  	return q.deliverTransferCancelEvent(handlers, p, evts)
1594  }
1595  
1596  func (q *pointerQueue) deliverTransferCancelEvent(handlers map[Tag]*routerHandler, p pointerInfo, evts []taggedEvent) (pointerInfo, []taggedEvent) {
1597  	evts = append(evts, taggedEvent{tag: p.dataSource, event: TransferCancelEvent{}})
1598  	src := &handlers[p.dataSource].filter.pointer
1599  	for k, h := range handlers {
1600  		if _, ok := firstMimeMatch(src, &h.filter.pointer); ok {
1601  			evts = append(evts, taggedEvent{tag: k, event: TransferCancelEvent{}})
1602  		}
1603  	}
1604  	p.dataSource = nil
1605  	p.dataTarget = nil
1606  	return p, evts
1607  }
1608  
1609  func (p *pointerFilter) Add(f Filter) {
1610  	switch f := f.(type) {
1611  	case PointerFilter:
1612  		p.kinds = p.kinds | f.Kinds
1613  		p.scrollX = p.scrollX.Union(f.ScrollX)
1614  		p.scrollY = p.scrollY.Union(f.ScrollY)
1615  	}
1616  }
1617  
1618  func (p *pointerFilter) AddTransfer(f Filter) {
1619  	switch f := f.(type) {
1620  	case TransferSourceFilter:
1621  		if !containsStr(p.sourceMimes, f.Type) {
1622  			p.sourceMimes = append(p.sourceMimes, f.Type)
1623  		}
1624  	case TransferTargetFilter:
1625  		if !containsStr(p.targetMimes, f.Type) {
1626  			p.targetMimes = append(p.targetMimes, f.Type)
1627  		}
1628  	}
1629  }
1630  
1631  func (p *pointerFilter) Matches(e Event) bool {
1632  	switch e := e.(type) {
1633  	case PointerEvent:
1634  		return e.Kind&p.kinds == e.Kind
1635  	case TransferCancelEvent, TransferInitiateEvent:
1636  		return len(p.sourceMimes) > 0 || len(p.targetMimes) > 0
1637  	case TransferRequestEvent:
1638  		return containsStr(p.sourceMimes, e.Type)
1639  	case TransferDataEvent:
1640  		return containsStr(p.targetMimes, e.Type)
1641  	}
1642  	return false
1643  }
1644  
1645  func (p *pointerFilter) Merge(p2 pointerFilter) {
1646  	p.kinds = p.kinds | p2.kinds
1647  	p.scrollX = p.scrollX.Union(p2.scrollX)
1648  	p.scrollY = p.scrollY.Union(p2.scrollY)
1649  	p.sourceMimes = append(p.sourceMimes, p2.sourceMimes...)
1650  	p.targetMimes = append(p.targetMimes, p2.targetMimes...)
1651  }
1652  
1653  func (p *pointerFilter) clampScroll(scroll Point) (left, scrolled Point) {
1654  	left.X, scrolled.X = clampSplit(scroll.X, p.scrollX.Min, p.scrollX.Max)
1655  	left.Y, scrolled.Y = clampSplit(scroll.Y, p.scrollY.Min, p.scrollY.Max)
1656  	return
1657  }
1658  
1659  func clampSplit(v float32, min, max int) (float32, float32) {
1660  	if m := float32(max); v > m {
1661  		return v - m, m
1662  	}
1663  	if m := float32(min); v < m {
1664  		return v - m, m
1665  	}
1666  	return 0, v
1667  }
1668  
1669  func (op *areaOp) Hit(pos Point) bool {
1670  	pos = pos.Sub(FPt(op.rect.Min))
1671  	size := FPt(op.rect.Size())
1672  	switch op.kind {
1673  	case areaRect:
1674  		return 0 <= pos.X && pos.X < size.X &&
1675  			0 <= pos.Y && pos.Y < size.Y
1676  	case areaEllipse:
1677  		rx := size.X / 2
1678  		ry := size.Y / 2
1679  		xh := pos.X - rx
1680  		yk := pos.Y - ry
1681  		return (xh*xh)/(rx*rx)+(yk*yk)/(ry*ry) <= 1
1682  	default:
1683  		panic("invalid area kind")
1684  	}
1685  }
1686  
1687  func (a *areaNode) bounds() image.Rectangle {
1688  	return Rectangle{
1689  		Min: a.trans.Transform(FPt(a.area.rect.Min)),
1690  		Max: a.trans.Transform(FPt(a.area.rect.Max)),
1691  	}.Round()
1692  }
1693  
1694  // ---------------------------------------------------------------------------
1695  // Key queue (ported from key.go)
1696  // ---------------------------------------------------------------------------
1697  
1698  type keyQueue struct {
1699  	order    []Tag
1700  	dirOrder []dirFocusEntry
1701  	hint     InputHint
1702  }
1703  
1704  type keyState struct {
1705  	focus   Tag
1706  	state   TextInputState
1707  	content EditorState
1708  }
1709  
1710  type keyHandler struct {
1711  	visible      bool
1712  	reset        bool
1713  	hint         InputHint
1714  	orderPlusOne int
1715  	dirOrder     int
1716  	trans        Affine2D
1717  }
1718  
1719  type keyFilterList []KeyFilter
1720  
1721  type dirFocusEntry struct {
1722  	tag    Tag
1723  	row    int
1724  	area   int
1725  	bounds image.Rectangle
1726  }
1727  
1728  func (k *keyHandler) inputHint(hint InputHint) { k.hint = hint }
1729  
1730  func (s keyState) InputState() (keyState, TextInputState) {
1731  	state := s.state
1732  	s.state = TextInputKeep
1733  	return s, state
1734  }
1735  
1736  func (q *keyQueue) InputHint(handlers map[Tag]*routerHandler, state keyState) (InputHint, bool) {
1737  	focused, ok := handlers[state.focus]
1738  	if !ok {
1739  		return q.hint, false
1740  	}
1741  	old := q.hint
1742  	q.hint = focused.key.hint
1743  	return q.hint, old != q.hint
1744  }
1745  
1746  func (k *keyHandler) Reset() {
1747  	k.visible = false
1748  	k.orderPlusOne = 0
1749  	k.hint = HintAny
1750  }
1751  
1752  func (q *keyQueue) Reset() {
1753  	q.order = q.order[:0]
1754  	q.dirOrder = q.dirOrder[:0]
1755  }
1756  
1757  func (k *keyHandler) ResetEvent() (Event, bool) {
1758  	if k.reset {
1759  		return nil, false
1760  	}
1761  	k.reset = true
1762  	return KeyFocusEvent{Focus: false}, true
1763  }
1764  
1765  func (q *keyQueue) Frame(handlers map[Tag]*routerHandler, state keyState) keyState {
1766  	if state.focus != nil {
1767  		if h, ok := handlers[state.focus]; !ok || !h.filter.focusable || !h.key.visible {
1768  			state.focus = nil
1769  			state.state = TextInputClose
1770  		}
1771  	}
1772  	q.updateFocusLayout(handlers)
1773  	return state
1774  }
1775  
1776  func (q *keyQueue) updateFocusLayout(handlers map[Tag]*routerHandler) {
1777  	order := q.dirOrder
1778  	// Sort by ascending Y - insertion sort (stable).
1779  	for i := 1; i < len(order); i++ {
1780  		for j := i; j > 0 && order[j].bounds.Min.Y < order[j-1].bounds.Min.Y; j-- {
1781  			order[j], order[j-1] = order[j-1], order[j]
1782  		}
1783  	}
1784  	row := 0
1785  	i := 0
1786  	for i < len(order) {
1787  		h := &order[i]
1788  		h.row = row
1789  		bottom := h.bounds.Max.Y
1790  		end := i + 1
1791  		for end < len(order) {
1792  			h2 := &order[end]
1793  			center := (h2.bounds.Min.Y + h2.bounds.Max.Y) / 2
1794  			if center > bottom {
1795  				break
1796  			}
1797  			h2.row = row
1798  			end++
1799  		}
1800  		// Sort row by ascending X.
1801  		for a := i + 1; a < end; a++ {
1802  			for b := a; b > i && order[b].bounds.Min.X < order[b-1].bounds.Min.X; b-- {
1803  				order[b], order[b-1] = order[b-1], order[b]
1804  			}
1805  		}
1806  		i = end
1807  		row++
1808  	}
1809  	for i, o := range q.dirOrder {
1810  		handlers[o.tag].key.dirOrder = i
1811  	}
1812  }
1813  
1814  func (q *keyQueue) MoveFocus(handlers map[Tag]*routerHandler, state keyState, dir FocusDirection) (keyState, []taggedEvent) {
1815  	if len(q.dirOrder) == 0 {
1816  		return state, nil
1817  	}
1818  	order := 0
1819  	if state.focus != nil {
1820  		order = handlers[state.focus].key.dirOrder
1821  	}
1822  	focus := q.dirOrder[order]
1823  	switch dir {
1824  	case FocusForward, FocusBackward:
1825  		if len(q.order) == 0 {
1826  			break
1827  		}
1828  		ord := 0
1829  		if dir == FocusBackward {
1830  			ord = -1
1831  		}
1832  		if state.focus != nil {
1833  			ord = handlers[state.focus].key.orderPlusOne - 1
1834  			if dir == FocusForward {
1835  				ord++
1836  			} else {
1837  				ord--
1838  			}
1839  		}
1840  		ord = (ord + len(q.order)) % len(q.order)
1841  		return q.Focus(handlers, state, q.order[ord])
1842  	case FocusRight, FocusLeft:
1843  		next := order
1844  		if state.focus != nil {
1845  			next = order + 1
1846  			if dir == FocusLeft {
1847  				next = order - 1
1848  			}
1849  		}
1850  		if 0 <= next && next < len(q.dirOrder) {
1851  			newFocus := q.dirOrder[next]
1852  			if newFocus.row == focus.row {
1853  				return q.Focus(handlers, state, newFocus.tag)
1854  			}
1855  		}
1856  	case FocusUp, FocusDown:
1857  		delta := +1
1858  		if dir == FocusUp {
1859  			delta = -1
1860  		}
1861  		nextRow := 0
1862  		if state.focus != nil {
1863  			nextRow = focus.row + delta
1864  		}
1865  		var closest Tag
1866  		dist := 1 << 30
1867  		center := (focus.bounds.Min.X + focus.bounds.Max.X) / 2
1868  		ord := order
1869  	focusLoop:
1870  		for 0 <= ord && ord < len(q.dirOrder) {
1871  			next := q.dirOrder[ord]
1872  			switch next.row {
1873  			case nextRow:
1874  				nextCenter := (next.bounds.Min.X + next.bounds.Max.X) / 2
1875  				d := center - nextCenter
1876  				if d < 0 {
1877  					d = -d
1878  				}
1879  				if d > dist {
1880  					break focusLoop
1881  				}
1882  				dist = d
1883  				closest = next.tag
1884  			case nextRow + delta:
1885  				break focusLoop
1886  			}
1887  			ord += delta
1888  		}
1889  		if closest != nil {
1890  			return q.Focus(handlers, state, closest)
1891  		}
1892  	}
1893  	return state, nil
1894  }
1895  
1896  func (q *keyQueue) BoundsFor(k *keyHandler) image.Rectangle {
1897  	return q.dirOrder[k.dirOrder].bounds
1898  }
1899  
1900  func (q *keyQueue) AreaFor(k *keyHandler) int {
1901  	return q.dirOrder[k.dirOrder].area
1902  }
1903  
1904  func (k *keyFilterList) Matches(focus Tag, e KeyEvent, system bool) bool {
1905  	for _, f := range *k {
1906  		if keyFilterMatch(focus, f, e, system) {
1907  			return true
1908  		}
1909  	}
1910  	return false
1911  }
1912  
1913  func keyFilterMatch(focus Tag, f KeyFilter, e KeyEvent, system bool) bool {
1914  	if f.Focus != nil && f.Focus != focus {
1915  		return false
1916  	}
1917  	if (f.Name != "" || system) && f.Name != e.Name {
1918  		return false
1919  	}
1920  	if e.Modifiers&f.Required != f.Required {
1921  		return false
1922  	}
1923  	if e.Modifiers&^(f.Required|f.Optional) != 0 {
1924  		return false
1925  	}
1926  	return true
1927  }
1928  
1929  func (q *keyQueue) Focus(handlers map[Tag]*routerHandler, state keyState, focus Tag) (keyState, []taggedEvent) {
1930  	if focus == state.focus {
1931  		return state, nil
1932  	}
1933  	state.content = EditorState{}
1934  	state.content.Selection.Transform = AffineId()
1935  	var evts []taggedEvent
1936  	if state.focus != nil {
1937  		evts = append(evts, taggedEvent{tag: state.focus, event: KeyFocusEvent{Focus: false}})
1938  	}
1939  	state.focus = focus
1940  	if state.focus != nil {
1941  		evts = append(evts, taggedEvent{tag: state.focus, event: KeyFocusEvent{Focus: true}})
1942  	}
1943  	if state.focus == nil || state.state == TextInputKeep {
1944  		state.state = TextInputClose
1945  	}
1946  	return state, evts
1947  }
1948  
1949  func (s keyState) softKeyboard(show bool) keyState {
1950  	if show {
1951  		s.state = TextInputOpen
1952  	} else {
1953  		s.state = TextInputClose
1954  	}
1955  	return s
1956  }
1957  
1958  func (k *keyFilterList) Add(f KeyFilter) {
1959  	for _, existing := range *k {
1960  		if existing == f {
1961  			return
1962  		}
1963  	}
1964  	*k = append(*k, f)
1965  }
1966  
1967  func (k *keyFilterList) Merge(k2 keyFilterList) {
1968  	*k = append(*k, k2...)
1969  }
1970  
1971  func (q *keyQueue) inputOp(tag Tag, state *keyHandler, t Affine2D, area int, bounds image.Rectangle) {
1972  	state.visible = true
1973  	if state.orderPlusOne == 0 {
1974  		state.orderPlusOne = len(q.order) + 1
1975  		q.order = append(q.order, tag)
1976  		q.dirOrder = append(q.dirOrder, dirFocusEntry{tag: tag, area: area, bounds: bounds})
1977  	}
1978  	state.trans = t
1979  }
1980  
1981  func (q *keyQueue) setSelection(state keyState, req SelectionCmd) keyState {
1982  	if req.Tag != state.focus {
1983  		return state
1984  	}
1985  	state.content.Selection.KeyRange = req.KeyRange
1986  	state.content.Selection.Caret = req.Caret
1987  	return state
1988  }
1989  
1990  func (q *keyQueue) editorState(handlers map[Tag]*routerHandler, state keyState) EditorState {
1991  	s := state.content
1992  	if f := state.focus; f != nil {
1993  		s.Selection.Transform = handlers[f].key.trans
1994  	}
1995  	return s
1996  }
1997  
1998  func (q *keyQueue) setSnippet(state keyState, req SnippetCmd) keyState {
1999  	if req.Tag == state.focus {
2000  		state.content.Snippet = req.Snippet
2001  	}
2002  	return state
2003  }
2004  
2005  // ---------------------------------------------------------------------------
2006  // Clipboard queue (ported from clipboard.go)
2007  // ---------------------------------------------------------------------------
2008  
2009  type clipboardState struct {
2010  	receivers []Tag
2011  }
2012  
2013  type clipboardQueue struct {
2014  	requested bool
2015  	mime      string
2016  	text      []byte
2017  }
2018  
2019  func (q *clipboardQueue) WriteClipboard() (mime string, content []byte, ok bool) {
2020  	if q.text == nil {
2021  		return "", nil, false
2022  	}
2023  	content = q.text
2024  	q.text = nil
2025  	return q.mime, content, true
2026  }
2027  
2028  func (q *clipboardQueue) ClipboardRequested(state clipboardState) bool {
2029  	req := len(state.receivers) > 0 && q.requested
2030  	q.requested = false
2031  	return req
2032  }
2033  
2034  func (q *clipboardQueue) Push(state clipboardState, e Event) (clipboardState, []taggedEvent) {
2035  	var evts []taggedEvent
2036  	for _, r := range state.receivers {
2037  		evts = append(evts, taggedEvent{tag: r, event: e})
2038  	}
2039  	state.receivers = nil
2040  	return state, evts
2041  }
2042  
2043  func (q *clipboardQueue) ProcessWriteClipboard(req ClipboardWriteCmd) {
2044  	if req.Data != nil {
2045  		content, err := io.ReadAll(req.Data)
2046  		req.Data.Close()
2047  		if err != nil {
2048  			return
2049  		}
2050  		q.mime = req.Type
2051  		q.text = content
2052  	}
2053  }
2054  
2055  func (q *clipboardQueue) ProcessReadClipboard(state clipboardState, tag Tag) clipboardState {
2056  	if containsTag(state.receivers, tag) {
2057  		return state
2058  	}
2059  	n := len(state.receivers)
2060  	state.receivers = append(state.receivers[:n:n], tag)
2061  	q.requested = true
2062  	return state
2063  }
2064  
2065  // ---------------------------------------------------------------------------
2066  // Translation: AppPointerEvent → PointerEvent, AppKeyEvent → KeyEvent
2067  // ---------------------------------------------------------------------------
2068  
2069  func appPointerToPointerEvent(e AppPointerEvent) PointerEvent {
2070  	var kind PointerKind
2071  	switch e.Kind {
2072  	case WinPointerMove:
2073  		kind = PointerMove
2074  	case WinPointerPress:
2075  		kind = PointerPress
2076  	case WinPointerRelease:
2077  		kind = PointerRelease
2078  	case WinPointerScroll:
2079  		kind = PointerScroll
2080  	case WinPointerCancel:
2081  		kind = PointerCancel
2082  	}
2083  	src := Mouse
2084  	touchID := PointerID(e.Buttons >> 8 & 0xff)
2085  	if touchID != 0 {
2086  		src = Touch
2087  	}
2088  	btns := PointerButtons(e.Buttons & 0xff)
2089  	var mods Modifiers
2090  	if e.Mods&ModAltBit != 0 {
2091  		mods |= ModAlt
2092  	}
2093  	if e.Mods&ModCtrlBit != 0 {
2094  		mods |= ModCtrl
2095  	}
2096  	if e.Mods&ModShiftBit != 0 {
2097  		mods |= ModShift
2098  	}
2099  	if e.Mods&ModMetaBit != 0 {
2100  		mods |= ModCommand
2101  	}
2102  	return PointerEvent{
2103  		Kind:      kind,
2104  		Source:    src,
2105  		PointerID: touchID,
2106  		Position:  Pt(e.X, e.Y),
2107  		Scroll:    Pt(e.Dx, e.Dy),
2108  		Buttons:   btns,
2109  		Modifiers: mods,
2110  	}
2111  }
2112  
2113  func appKeyToKeyEvent(e AppKeyEvent) (KeyEvent, bool) {
2114  	name := keyCodeToKeyName(e.Code, e.Mods)
2115  	if name == "" {
2116  		return KeyEvent{}, false
2117  	}
2118  	var mods Modifiers
2119  	if e.Mods&ModAltBit != 0 {
2120  		mods |= ModAlt
2121  	}
2122  	if e.Mods&ModCtrlBit != 0 {
2123  		mods |= ModCtrl
2124  	}
2125  	if e.Mods&ModShiftBit != 0 {
2126  		mods |= ModShift
2127  	}
2128  	if e.Mods&ModMetaBit != 0 {
2129  		mods |= ModCommand
2130  	}
2131  	state := KeyRelease
2132  	if e.Press {
2133  		state = KeyPress
2134  	}
2135  	return KeyEvent{Name: name, Modifiers: mods, State: state}, true
2136  }
2137  
2138  // keyCodeToKeyName maps AppKeyEvent.Code to a KeyName.
2139  func keyCodeToKeyName(code, mods int32) KeyName {
2140  	switch code {
2141  	case KeyArrowUp:
2142  		return KeyNameUpArrow
2143  	case KeyArrowDown:
2144  		return KeyNameDownArrow
2145  	case KeyArrowLeft:
2146  		return KeyNameLeftArrow
2147  	case KeyArrowRight:
2148  		return KeyNameRightArrow
2149  	case KeyEscape:
2150  		return KeyNameEscape
2151  	case KeyEnter:
2152  		return KeyNameReturn
2153  	case KeyBackspace:
2154  		return KeyNameDeleteBackward
2155  	case KeyDelete:
2156  		return KeyNameDeleteForward
2157  	case KeyHome:
2158  		return KeyNameHome
2159  	case KeyEnd:
2160  		return KeyNameEnd
2161  	case KeyPageUp:
2162  		return KeyNamePageUp
2163  	case KeyPageDown:
2164  		return KeyNamePageDown
2165  	case KeyTab:
2166  		return KeyNameTab
2167  	case KeySpace:
2168  		return KeyNameSpace
2169  	case KeyF1:
2170  		return KeyNameF1
2171  	case KeyF1 + 1:
2172  		return KeyNameF2
2173  	case KeyF1 + 2:
2174  		return KeyNameF3
2175  	case KeyF1 + 3:
2176  		return KeyNameF4
2177  	case KeyF1 + 4:
2178  		return KeyNameF5
2179  	case KeyF1 + 5:
2180  		return KeyNameF6
2181  	case KeyF1 + 6:
2182  		return KeyNameF7
2183  	case KeyF1 + 7:
2184  		return KeyNameF8
2185  	case KeyF1 + 8:
2186  		return KeyNameF9
2187  	case KeyF1 + 9:
2188  		return KeyNameF10
2189  	case KeyF1 + 10:
2190  		return KeyNameF11
2191  	case KeyF12:
2192  		return KeyNameF12
2193  	case KeyCtrl:
2194  		return KeyNameCtrl
2195  	case KeyShift:
2196  		return KeyNameShift
2197  	case KeyAlt:
2198  		return KeyNameAlt
2199  	case KeySuper:
2200  		return KeyNameSuper
2201  	case KeyCommand:
2202  		return KeyNameCommand
2203  	}
2204  	// Printable ASCII: code is uppercase codepoint.
2205  	if code >= 0x20 && code <= 0x7e {
2206  		// Apply shift for lowercase vs uppercase.
2207  		if mods&ModShiftBit == 0 && code >= 'A' && code <= 'Z' {
2208  			// return lowercase
2209  			return KeyName(string(rune(code + 32)))
2210  		}
2211  		return KeyName(string(rune(code)))
2212  	}
2213  	return ""
2214  }
2215  
2216  // ---------------------------------------------------------------------------
2217  // Slice helpers (replaces slices package)
2218  // ---------------------------------------------------------------------------
2219  
2220  func containsTag(tags []Tag, tag Tag) bool {
2221  	for _, t := range tags {
2222  		if t == tag {
2223  			return true
2224  		}
2225  	}
2226  	return false
2227  }
2228  
2229  func containsStr(ss []string, s string) bool {
2230  	for _, v := range ss {
2231  		if v == s {
2232  			return true
2233  		}
2234  	}
2235  	return false
2236  }
2237  
2238  func searchTag(tags []Tag, tag Tag) (int, bool) {
2239  	for i, t := range tags {
2240  		if t == tag {
2241  			return i, true
2242  		}
2243  	}
2244  	return 0, false
2245  }
2246  
2247  func addTagToList(tags []Tag, tag Tag) []Tag {
2248  	if containsTag(tags, tag) {
2249  		return tags
2250  	}
2251  	return append(tags, tag)
2252  }
2253  
2254  func firstMimeMatch(src, tgt *pointerFilter) (first string, matched bool) {
2255  	for _, m1 := range tgt.targetMimes {
2256  		if containsStr(src.sourceMimes, m1) {
2257  			return m1, true
2258  		}
2259  	}
2260  	return "", false
2261  }
2262  
2263  func clonePointers(s []pointerInfo) []pointerInfo {
2264  	c := []pointerInfo{:len(s):len(s)}
2265  	copy(c, s)
2266  	return c
2267  }
2268  
2269  func concatPointers(a, b []pointerInfo) []pointerInfo {
2270  	r := []pointerInfo{:len(a)+len(b):len(a)+len(b)}
2271  	copy(r, a)
2272  	copy(r[len(a):], b)
2273  	return r
2274  }
2275  
2276  func deleteTaggedEvent(s []taggedEvent, i, j int) []taggedEvent {
2277  	copy(s[i:], s[j:])
2278  	return s[:len(s)-(j-i)]
2279  }
2280  
2281  func deleteSemanticID(s []semanticID, i, j int) []semanticID {
2282  	copy(s[i:], s[j:])
2283  	return s[:len(s)-(j-i)]
2284  }
2285