gioui.org/ui@v0.0.0-20190926171558-ce74bc0cbaea/app/internal/input/router.go (about)

     1  // SPDX-License-Identifier: Unlicense OR MIT
     2  
     3  package input
     4  
     5  import (
     6  	"encoding/binary"
     7  	"time"
     8  
     9  	"gioui.org/ui"
    10  	"gioui.org/ui/internal/opconst"
    11  	"gioui.org/ui/internal/ops"
    12  	"gioui.org/ui/key"
    13  	"gioui.org/ui/pointer"
    14  	"gioui.org/ui/system"
    15  )
    16  
    17  // Router is a Queue implementation that routes events from
    18  // all available input sources to registered handlers.
    19  type Router struct {
    20  	pqueue pointerQueue
    21  	kqueue keyQueue
    22  
    23  	handlers handlerEvents
    24  
    25  	reader ops.Reader
    26  
    27  	// deliveredEvents tracks whether events has been returned to the
    28  	// user from Events. If so, another frame is scheduled to flush
    29  	// half-updated state. This is important when a an event changes
    30  	// UI state that has already been laid out. In the worst case, we
    31  	// waste a frame, increasing power usage.
    32  	// Gio is expected to grow the ability to construct frame-to-frame
    33  	// differences and only render to changed areas. In that case, the
    34  	// waste of a spurious frame should be minimal.
    35  	deliveredEvents bool
    36  	// InvalidateOp summary.
    37  	wakeup     bool
    38  	wakeupTime time.Time
    39  
    40  	// ProfileOp summary.
    41  	profHandlers []ui.Key
    42  }
    43  
    44  type handlerEvents struct {
    45  	handlers  map[ui.Key][]ui.Event
    46  	hadEvents bool
    47  }
    48  
    49  func (q *Router) Events(k ui.Key) []ui.Event {
    50  	events := q.handlers.Events(k)
    51  	q.deliveredEvents = q.deliveredEvents || len(events) > 0
    52  	return events
    53  }
    54  
    55  func (q *Router) Frame(ops *ui.Ops) {
    56  	q.handlers.Clear()
    57  	q.wakeup = false
    58  	q.profHandlers = q.profHandlers[:0]
    59  	q.reader.Reset(ops)
    60  	q.collect()
    61  
    62  	q.pqueue.Frame(ops, &q.handlers)
    63  	q.kqueue.Frame(ops, &q.handlers)
    64  	if q.deliveredEvents || q.handlers.HadEvents() {
    65  		q.deliveredEvents = false
    66  		q.wakeup = true
    67  		q.wakeupTime = time.Time{}
    68  	}
    69  }
    70  
    71  func (q *Router) Add(e ui.Event) bool {
    72  	switch e := e.(type) {
    73  	case pointer.Event:
    74  		q.pqueue.Push(e, &q.handlers)
    75  	case key.EditEvent, key.Event, key.FocusEvent:
    76  		q.kqueue.Push(e, &q.handlers)
    77  	}
    78  	return q.handlers.HadEvents()
    79  }
    80  
    81  func (q *Router) TextInputState() TextInputState {
    82  	return q.kqueue.InputState()
    83  }
    84  
    85  func (q *Router) collect() {
    86  	for encOp, ok := q.reader.Decode(); ok; encOp, ok = q.reader.Decode() {
    87  		switch opconst.OpType(encOp.Data[0]) {
    88  		case opconst.TypeInvalidate:
    89  			op := decodeInvalidateOp(encOp.Data)
    90  			if !q.wakeup || op.At.Before(q.wakeupTime) {
    91  				q.wakeup = true
    92  				q.wakeupTime = op.At
    93  			}
    94  		case opconst.TypeProfile:
    95  			op := decodeProfileOp(encOp.Data, encOp.Refs)
    96  			q.profHandlers = append(q.profHandlers, op.Key)
    97  		}
    98  	}
    99  }
   100  
   101  func (q *Router) AddProfile(e system.ProfileEvent) {
   102  	for _, h := range q.profHandlers {
   103  		q.handlers.Add(h, e)
   104  	}
   105  }
   106  
   107  func (q *Router) Profiling() bool {
   108  	return len(q.profHandlers) > 0
   109  }
   110  
   111  func (q *Router) WakeupTime() (time.Time, bool) {
   112  	return q.wakeupTime, q.wakeup
   113  }
   114  
   115  func (h *handlerEvents) init() {
   116  	if h.handlers == nil {
   117  		h.handlers = make(map[ui.Key][]ui.Event)
   118  	}
   119  }
   120  
   121  func (h *handlerEvents) Set(k ui.Key, evts []ui.Event) {
   122  	h.init()
   123  	h.handlers[k] = evts
   124  	h.hadEvents = true
   125  }
   126  
   127  func (h *handlerEvents) Add(k ui.Key, e ui.Event) {
   128  	h.init()
   129  	h.handlers[k] = append(h.handlers[k], e)
   130  	h.hadEvents = true
   131  }
   132  
   133  func (h *handlerEvents) HadEvents() bool {
   134  	u := h.hadEvents
   135  	h.hadEvents = false
   136  	return u
   137  }
   138  
   139  func (h *handlerEvents) Events(k ui.Key) []ui.Event {
   140  	if events, ok := h.handlers[k]; ok {
   141  		h.handlers[k] = h.handlers[k][:0]
   142  		return events
   143  	}
   144  	return nil
   145  }
   146  
   147  func (h *handlerEvents) Clear() {
   148  	for k := range h.handlers {
   149  		delete(h.handlers, k)
   150  	}
   151  }
   152  
   153  func decodeProfileOp(d []byte, refs []interface{}) system.ProfileOp {
   154  	if opconst.OpType(d[0]) != opconst.TypeProfile {
   155  		panic("invalid op")
   156  	}
   157  	return system.ProfileOp{
   158  		Key: refs[0].(ui.Key),
   159  	}
   160  }
   161  
   162  func decodeInvalidateOp(d []byte) ui.InvalidateOp {
   163  	bo := binary.LittleEndian
   164  	if opconst.OpType(d[0]) != opconst.TypeInvalidate {
   165  		panic("invalid op")
   166  	}
   167  	var o ui.InvalidateOp
   168  	if nanos := bo.Uint64(d[1:]); nanos > 0 {
   169  		o.At = time.Unix(0, int64(nanos))
   170  	}
   171  	return o
   172  }