Files
gio/ui/app/internal/input/router.go
T
Elias Naur 340fff9814 ui/key: rename ChordEvent to just Event
Event is like pointer.Event and we don't want the stuttering of
key.KeyEvent.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2019-08-11 12:50:40 +02:00

164 lines
3.3 KiB
Go

// SPDX-License-Identifier: Unlicense OR MIT
package input
import (
"encoding/binary"
"time"
"gioui.org/ui"
"gioui.org/ui/input"
"gioui.org/ui/internal/opconst"
"gioui.org/ui/internal/ops"
"gioui.org/ui/key"
"gioui.org/ui/pointer"
"gioui.org/ui/system"
)
// Router is a Queue implementation that routes events from
// all available input sources to registered handlers.
type Router struct {
pqueue pointerQueue
kqueue keyQueue
handlers handlerEvents
reader ops.Reader
// InvalidateOp summary.
wakeup bool
wakeupTime time.Time
// ProfileOp summary.
profHandlers []input.Key
}
type handlerEvents struct {
handlers map[input.Key][]input.Event
updated bool
}
func (q *Router) Next(k input.Key) (input.Event, bool) {
return q.handlers.Next(k)
}
func (q *Router) Frame(ops *ui.Ops) {
q.handlers.Clear()
q.wakeup = false
q.profHandlers = q.profHandlers[:0]
q.reader.Reset(ops)
q.collect()
q.pqueue.Frame(ops, &q.handlers)
q.kqueue.Frame(ops, &q.handlers)
if q.handlers.Updated() {
q.wakeup = true
q.wakeupTime = time.Time{}
}
}
func (q *Router) Add(e input.Event) bool {
switch e := e.(type) {
case pointer.Event:
q.pqueue.Push(e, &q.handlers)
case key.EditEvent, key.Event, key.FocusEvent:
q.kqueue.Push(e, &q.handlers)
}
return q.handlers.Updated()
}
func (q *Router) TextInputState() TextInputState {
return q.kqueue.InputState()
}
func (q *Router) collect() {
for encOp, ok := q.reader.Decode(); ok; encOp, ok = q.reader.Decode() {
switch opconst.OpType(encOp.Data[0]) {
case opconst.TypeInvalidate:
op := decodeInvalidateOp(encOp.Data)
if !q.wakeup || op.At.Before(q.wakeupTime) {
q.wakeup = true
q.wakeupTime = op.At
}
case opconst.TypeProfile:
op := decodeProfileOp(encOp.Data, encOp.Refs)
q.profHandlers = append(q.profHandlers, op.Key)
}
}
}
func (q *Router) AddProfile(e system.ProfileEvent) {
for _, h := range q.profHandlers {
q.handlers.Add(h, e)
}
}
func (q *Router) Profiling() bool {
return len(q.profHandlers) > 0
}
func (q *Router) WakeupTime() (time.Time, bool) {
return q.wakeupTime, q.wakeup
}
func (h *handlerEvents) init() {
if h.handlers == nil {
h.handlers = make(map[input.Key][]input.Event)
}
}
func (h *handlerEvents) Set(k input.Key, evts []input.Event) {
h.init()
h.handlers[k] = evts
h.updated = true
}
func (h *handlerEvents) Add(k input.Key, e input.Event) {
h.init()
h.handlers[k] = append(h.handlers[k], e)
h.updated = true
}
func (h *handlerEvents) Updated() bool {
u := h.updated
h.updated = false
return u
}
func (h *handlerEvents) Next(k input.Key) (input.Event, bool) {
events := h.handlers[k]
if len(events) == 0 {
return nil, false
}
e := events[0]
h.handlers[k] = events[1:]
return e, true
}
func (h *handlerEvents) Clear() {
for k := range h.handlers {
delete(h.handlers, k)
}
}
func decodeProfileOp(d []byte, refs []interface{}) system.ProfileOp {
if opconst.OpType(d[0]) != opconst.TypeProfile {
panic("invalid op")
}
return system.ProfileOp{
Key: refs[0].(input.Key),
}
}
func decodeInvalidateOp(d []byte) ui.InvalidateOp {
bo := binary.LittleEndian
if opconst.OpType(d[0]) != opconst.TypeInvalidate {
panic("invalid op")
}
var o ui.InvalidateOp
if nanos := bo.Uint64(d[1:]); nanos > 0 {
o.At = time.Unix(0, int64(nanos))
}
return o
}