Files
gio/ui/key/queue.go
T
Elias Naur 252e058766 all: serialize ops
Pros:
- Much less per-frame garbage
- Allow future preprocessing of ops while building it
- Much fewer interface calls and pointer chasing
- Allow future serialization of ops for remote rendering

Cons:
- Slightly clumsier API

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2019-04-27 22:19:34 +02:00

117 lines
2.1 KiB
Go

// SPDX-License-Identifier: Unlicense OR MIT
package key
import (
"gioui.org/ui"
"gioui.org/ui/internal/ops"
)
type Queue struct {
focus Key
events []Event
handlers map[Key]bool
reader ops.Reader
}
type listenerPriority uint8
const (
priNone listenerPriority = iota
priDefault
priCurrentFocus
priNewFocus
)
func (q *Queue) Frame(root *ui.Ops) TextInputState {
q.events = q.events[:0]
q.reader.Reset(root.Data(), root.Refs())
f, pri, hide := resolveFocus(&q.reader, q.focus)
changed := f != nil && f != q.focus
for k, active := range q.handlers {
if !active || changed {
delete(q.handlers, k)
} else {
q.handlers[k] = false
}
}
q.focus = f
switch {
case pri == priNewFocus:
return TextInputOpen
case hide:
return TextInputClosed
case changed:
return TextInputFocus
default:
return TextInputKeep
}
}
func (q *Queue) Push(e Event) {
q.events = append(q.events, e)
}
func (q *Queue) For(k Key) []Event {
if q.handlers == nil {
q.handlers = make(map[Key]bool)
}
_, exists := q.handlers[k]
q.handlers[k] = true
if !exists {
if k == q.focus {
// Prepend focus event.
q.events = append(q.events, nil)
copy(q.events[1:], q.events)
q.events[0] = Focus{Focus: true}
} else {
return []Event{Focus{Focus: false}}
}
}
if k != q.focus {
return nil
}
return q.events
}
func resolveFocus(r *ops.Reader, focus Key) (Key, listenerPriority, bool) {
var k Key
var pri listenerPriority
var hide bool
loop:
for {
data, ok := r.Decode()
if !ok {
break
}
switch ops.OpType(data[0]) {
case ops.TypeKeyHandler:
var op OpHandler
op.Decode(data, r.Refs)
var newPri listenerPriority
switch {
case op.Focus:
newPri = priNewFocus
case op.Key == focus:
newPri = priCurrentFocus
default:
newPri = priDefault
}
if newPri >= pri {
k, pri = op.Key, newPri
}
case ops.TypeHideInput:
hide = true
case ops.TypePush:
newK, newPri, h := resolveFocus(r, focus)
hide = hide || h
if newPri >= pri {
k, pri = newK, newPri
}
case ops.TypePop:
break loop
}
}
return k, pri, hide
}