mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-07 10:25:37 +00:00
ui: add package input for merged input
To avoid passing a queue type for each kind of input (pointer, key), introduce package input for mapping a handler key to all input events. Future input sources can be added without changes to programs, and as an added bonus, event ordering is preserved across input sources. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+6
-14
@@ -16,14 +16,6 @@ type OpHideInput struct{}
|
||||
|
||||
type Key interface{}
|
||||
|
||||
type Events interface {
|
||||
For(k Key) []Event
|
||||
}
|
||||
|
||||
type Event interface {
|
||||
isKeyEvent()
|
||||
}
|
||||
|
||||
type Focus struct {
|
||||
Focus bool
|
||||
}
|
||||
@@ -93,9 +85,9 @@ func (h OpHideInput) Add(o *ui.Ops) {
|
||||
o.Write(data)
|
||||
}
|
||||
|
||||
func (Edit) ImplementsEvent() {}
|
||||
func (Chord) ImplementsEvent() {}
|
||||
func (Focus) ImplementsEvent() {}
|
||||
func (Edit) isKeyEvent() {}
|
||||
func (Chord) isKeyEvent() {}
|
||||
func (Focus) isKeyEvent() {}
|
||||
func (Edit) ImplementsEvent() {}
|
||||
func (Chord) ImplementsEvent() {}
|
||||
func (Focus) ImplementsEvent() {}
|
||||
func (Edit) ImplementsInputEvent() {}
|
||||
func (Chord) ImplementsInputEvent() {}
|
||||
func (Focus) ImplementsInputEvent() {}
|
||||
|
||||
-145
@@ -1,145 +0,0 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package key
|
||||
|
||||
import (
|
||||
"gioui.org/ui"
|
||||
"gioui.org/ui/internal/ops"
|
||||
)
|
||||
|
||||
type Queue struct {
|
||||
focus Key
|
||||
handlers map[Key]*handler
|
||||
reader ui.OpsReader
|
||||
state TextInputState
|
||||
}
|
||||
|
||||
type handler struct {
|
||||
active bool
|
||||
events []Event
|
||||
}
|
||||
|
||||
type listenerPriority uint8
|
||||
|
||||
const (
|
||||
priNone listenerPriority = iota
|
||||
priDefault
|
||||
priCurrentFocus
|
||||
priNewFocus
|
||||
)
|
||||
|
||||
// InputState returns the last text input state as
|
||||
// determined in Frame.
|
||||
func (q *Queue) InputState() TextInputState {
|
||||
return q.state
|
||||
}
|
||||
|
||||
func (q *Queue) Frame(root *ui.Ops) {
|
||||
if q.handlers == nil {
|
||||
q.handlers = make(map[Key]*handler)
|
||||
}
|
||||
for _, h := range q.handlers {
|
||||
h.active = false
|
||||
h.events = h.events[:0]
|
||||
}
|
||||
q.reader.Reset(root)
|
||||
focus, pri, hide := q.resolveFocus()
|
||||
for k, h := range q.handlers {
|
||||
if !h.active {
|
||||
delete(q.handlers, k)
|
||||
if q.focus == k {
|
||||
q.focus = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
changed := focus != nil && focus != q.focus
|
||||
if focus != q.focus {
|
||||
if q.focus != nil {
|
||||
if h, ok := q.handlers[q.focus]; ok {
|
||||
h.events = append(h.events, Focus{Focus: false})
|
||||
}
|
||||
}
|
||||
q.focus = focus
|
||||
if q.focus != nil {
|
||||
// A new focus always exists in the handler map.
|
||||
h := q.handlers[q.focus]
|
||||
h.events = append(h.events, Focus{Focus: true})
|
||||
}
|
||||
}
|
||||
switch {
|
||||
case pri == priNewFocus:
|
||||
q.state = TextInputOpen
|
||||
case hide:
|
||||
q.state = TextInputClosed
|
||||
case changed:
|
||||
q.state = TextInputFocus
|
||||
default:
|
||||
q.state = TextInputKeep
|
||||
}
|
||||
}
|
||||
|
||||
func (q *Queue) Push(e Event) {
|
||||
if q.focus == nil {
|
||||
return
|
||||
}
|
||||
h := q.handlers[q.focus]
|
||||
h.events = append(h.events, e)
|
||||
}
|
||||
|
||||
func (q *Queue) For(k Key) []Event {
|
||||
h := q.handlers[k]
|
||||
if h == nil {
|
||||
return nil
|
||||
}
|
||||
return h.events
|
||||
}
|
||||
|
||||
func (q *Queue) resolveFocus() (Key, listenerPriority, bool) {
|
||||
var k Key
|
||||
var pri listenerPriority
|
||||
var hide bool
|
||||
loop:
|
||||
for {
|
||||
encOp, ok := q.reader.Decode()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
switch ops.OpType(encOp.Data[0]) {
|
||||
case ops.TypeKeyHandler:
|
||||
var op OpHandler
|
||||
op.Decode(encOp.Data, encOp.Refs)
|
||||
var newPri listenerPriority
|
||||
switch {
|
||||
case op.Focus:
|
||||
newPri = priNewFocus
|
||||
case op.Key == q.focus:
|
||||
newPri = priCurrentFocus
|
||||
default:
|
||||
newPri = priDefault
|
||||
}
|
||||
if newPri >= pri {
|
||||
k, pri = op.Key, newPri
|
||||
}
|
||||
h, ok := q.handlers[op.Key]
|
||||
if !ok {
|
||||
h = &handler{
|
||||
// Reset the handler on (each) first appearance.
|
||||
events: []Event{Focus{Focus: false}},
|
||||
}
|
||||
q.handlers[op.Key] = h
|
||||
}
|
||||
h.active = true
|
||||
case ops.TypeHideInput:
|
||||
hide = true
|
||||
case ops.TypePush:
|
||||
newK, newPri, h := q.resolveFocus()
|
||||
hide = hide || h
|
||||
if newPri >= pri {
|
||||
k, pri = newK, newPri
|
||||
}
|
||||
case ops.TypePop:
|
||||
break loop
|
||||
}
|
||||
}
|
||||
return k, pri, hide
|
||||
}
|
||||
Reference in New Issue
Block a user