Files
gio/ui/app/internal/input/router.go
T
Elias Naur 73b1e64209 ui/input: change Queue to return Events one at a time
By returning all events, widgets that might return early from its
event loop might throw away subsequent events. Instead of requiring
those widgets to store the event list, convert input.Queue to step
through the available events one at a time.

Functional revert of 1735d5ced8.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2019-08-01 18:56:49 +02:00

143 lines
2.8 KiB
Go

// SPDX-License-Identifier: Unlicense OR MIT
package input
import (
"time"
"gioui.org/ui"
"gioui.org/ui/input"
"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 ui.OpsReader
// 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.ChordEvent, 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 ops.OpType(encOp.Data[0]) {
case ops.TypeInvalidate:
var op ui.InvalidateOp
op.Decode(encOp.Data)
if !q.wakeup || op.At.Before(q.wakeupTime) {
q.wakeup = true
q.wakeupTime = op.At
}
case ops.TypeProfile:
var op system.ProfileOp
op.Decode(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)
}
}