github.com/utopiagio/gio@v0.0.8/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  	"github.com/utopiagio/gio/f32"
    12  	f32internal "github.com/utopiagio/gio/internal/f32"
    13  	"github.com/utopiagio/gio/internal/ops"
    14  	"github.com/utopiagio/gio/io/clipboard"
    15  	"github.com/utopiagio/gio/io/event"
    16  	"github.com/utopiagio/gio/io/key"
    17  	"github.com/utopiagio/gio/io/pointer"
    18  	"github.com/utopiagio/gio/io/semantic"
    19  	"github.com/utopiagio/gio/io/system"
    20  	"github.com/utopiagio/gio/io/transfer"
    21  	"github.com/utopiagio/gio/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() {}