Files
gio/ui/key/queue.go
T
Elias Naur 5966aab77e ui: move ops reader to ui package
To prepare support for cached OpBlock to refer to other Ops lists.

The exposure of OpsReader is alleviated by the removal of the Refs
and Data accessors for Ops.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2019-06-02 19:17:53 +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 ui.OpsReader
}
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)
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 *ui.OpsReader, 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
}