mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-02 16:06:19 +00:00
73b1e64209
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>
143 lines
2.8 KiB
Go
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)
|
|
}
|
|
}
|