mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
io/router/key: add explicit tag to FocusOp; make last SoftKeyboardOp apply
The target of FocusOp is too subtle; be explicit instead and remove any doubt. Multiple SoftKeyboardOp in a single frame is rare, but if they do occur, they should behave as if they were from separate frames: the last one applies. As a side-effect the key event router can be much simplified. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+7
-8
@@ -26,15 +26,17 @@ type InputOp struct {
|
||||
}
|
||||
|
||||
// SoftKeyboardOp shows or hide the on-screen keyboard, if available.
|
||||
// It replaces any previous SoftKeyboardOp.
|
||||
type SoftKeyboardOp struct {
|
||||
Show bool
|
||||
}
|
||||
|
||||
// FocusOp sets or clears the keyboard focus.
|
||||
// FocusOp sets or clears the keyboard focus. It replaces any previous
|
||||
// FocusOp in the same frame.
|
||||
type FocusOp struct {
|
||||
// Focus, if set, moves the focus to the current InputOp. If Focus
|
||||
// is false, the focus is cleared.
|
||||
Focus bool
|
||||
// Tag is the new focus. The focus is cleared if Tag is nil, or if Tag
|
||||
// has no InputOp in the same frame.
|
||||
Tag event.Tag
|
||||
}
|
||||
|
||||
// A FocusEvent is generated when a handler gains or loses
|
||||
@@ -132,11 +134,8 @@ func (h SoftKeyboardOp) Add(o *op.Ops) {
|
||||
}
|
||||
|
||||
func (h FocusOp) Add(o *op.Ops) {
|
||||
data := o.Write(opconst.TypeKeyFocusLen)
|
||||
data := o.Write1(opconst.TypeKeyFocusLen, h.Tag)
|
||||
data[0] = byte(opconst.TypeKeyFocus)
|
||||
if h.Focus {
|
||||
data[1] = 1
|
||||
}
|
||||
}
|
||||
|
||||
func (EditEvent) ImplementsEvent() {}
|
||||
|
||||
+20
-68
@@ -17,14 +17,6 @@ type keyQueue struct {
|
||||
handlers map[event.Tag]*keyHandler
|
||||
reader ops.Reader
|
||||
state TextInputState
|
||||
// states store states during resolveFocus
|
||||
states []resolveState
|
||||
}
|
||||
|
||||
type resolveState struct {
|
||||
tag event.Tag
|
||||
pri listenerPriority
|
||||
keyboard TextInputState
|
||||
}
|
||||
|
||||
type keyHandler struct {
|
||||
@@ -34,15 +26,6 @@ type keyHandler struct {
|
||||
new bool
|
||||
}
|
||||
|
||||
type listenerPriority uint8
|
||||
|
||||
const (
|
||||
priDefault listenerPriority = iota
|
||||
priCurrentFocus
|
||||
priNone
|
||||
priNewFocus
|
||||
)
|
||||
|
||||
const (
|
||||
TextInputKeep TextInputState = iota
|
||||
TextInputClose
|
||||
@@ -64,36 +47,37 @@ func (q *keyQueue) Frame(root *op.Ops, events *handlerEvents) {
|
||||
}
|
||||
q.reader.Reset(root)
|
||||
|
||||
state := q.resolveFocus(events)
|
||||
if state.pri == priNone {
|
||||
state.tag = nil
|
||||
}
|
||||
focus, changed, state := q.resolveFocus(events)
|
||||
for k, h := range q.handlers {
|
||||
if !h.visible {
|
||||
delete(q.handlers, k)
|
||||
if q.focus == k {
|
||||
// Remove the focus from the handler that is no longer visible.
|
||||
q.focus = nil
|
||||
state.keyboard = TextInputClose
|
||||
state = TextInputClose
|
||||
}
|
||||
}
|
||||
if h.new && k != state.tag {
|
||||
// Reset the handler on (each) first appearance.
|
||||
} else if h.new && k != focus {
|
||||
// Reset the handler on (each) first appearance, but don't trigger redraw.
|
||||
events.Add(k, key.FocusEvent{Focus: false})
|
||||
}
|
||||
}
|
||||
if state.tag != q.focus {
|
||||
if changed && focus != nil {
|
||||
if _, exists := q.handlers[focus]; !exists {
|
||||
focus = nil
|
||||
}
|
||||
}
|
||||
if changed && focus != q.focus {
|
||||
if q.focus != nil {
|
||||
events.Add(q.focus, key.FocusEvent{Focus: false})
|
||||
}
|
||||
q.focus = state.tag
|
||||
q.focus = focus
|
||||
if q.focus != nil {
|
||||
events.Add(q.focus, key.FocusEvent{Focus: true})
|
||||
} else {
|
||||
state.keyboard = TextInputClose
|
||||
state = TextInputClose
|
||||
}
|
||||
}
|
||||
q.state = state.keyboard
|
||||
q.state = state
|
||||
}
|
||||
|
||||
func (q *keyQueue) Push(e event.Event, events *handlerEvents) {
|
||||
@@ -102,63 +86,31 @@ func (q *keyQueue) Push(e event.Event, events *handlerEvents) {
|
||||
}
|
||||
}
|
||||
|
||||
func (q *keyQueue) resolveFocus(events *handlerEvents) resolveState {
|
||||
var state resolveState
|
||||
func (q *keyQueue) resolveFocus(events *handlerEvents) (focus event.Tag, changed bool, state TextInputState) {
|
||||
for encOp, ok := q.reader.Decode(); ok; encOp, ok = q.reader.Decode() {
|
||||
switch opconst.OpType(encOp.Data[0]) {
|
||||
case opconst.TypeKeyFocus:
|
||||
op := decodeFocusOp(encOp.Data, encOp.Refs)
|
||||
if op.Focus {
|
||||
state.pri = priNewFocus
|
||||
} else {
|
||||
state.pri, state.keyboard = priNone, TextInputClose
|
||||
}
|
||||
changed = true
|
||||
focus = op.Tag
|
||||
case opconst.TypeKeySoftKeyboard:
|
||||
op := decodeSoftKeyboardOp(encOp.Data, encOp.Refs)
|
||||
if op.Show {
|
||||
state.keyboard = TextInputOpen
|
||||
state = TextInputOpen
|
||||
} else {
|
||||
state.keyboard = TextInputClose
|
||||
state = TextInputClose
|
||||
}
|
||||
case opconst.TypeKeyInput:
|
||||
op := decodeKeyInputOp(encOp.Data, encOp.Refs)
|
||||
if op.Tag == q.focus && state.pri < priCurrentFocus {
|
||||
state.pri = priCurrentFocus
|
||||
}
|
||||
h, ok := q.handlers[op.Tag]
|
||||
if !ok {
|
||||
h = &keyHandler{new: true}
|
||||
q.handlers[op.Tag] = h
|
||||
}
|
||||
h.visible = true
|
||||
state.tag = op.Tag
|
||||
case opconst.TypeSave:
|
||||
id := ops.DecodeSave(encOp.Data)
|
||||
if extra := id - len(q.states) + 1; extra > 0 {
|
||||
q.states = append(q.states, make([]resolveState, extra)...)
|
||||
}
|
||||
q.states[id] = state
|
||||
state = resolveState{}
|
||||
case opconst.TypeLoad:
|
||||
id, mask := ops.DecodeLoad(encOp.Data)
|
||||
restored := q.states[id]
|
||||
if state.keyboard > restored.keyboard {
|
||||
restored.keyboard = state.keyboard
|
||||
}
|
||||
if state.pri.replaces(restored.pri) {
|
||||
restored.tag, restored.pri = state.tag, state.pri
|
||||
}
|
||||
if mask != 0 {
|
||||
state = restored
|
||||
}
|
||||
}
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
func (p listenerPriority) replaces(p2 listenerPriority) bool {
|
||||
// Favor earliest default focus or latest requested focus.
|
||||
return p > p2 || p == p2 && p == priNewFocus
|
||||
return
|
||||
}
|
||||
|
||||
func decodeKeyInputOp(d []byte, refs []interface{}) key.InputOp {
|
||||
@@ -184,6 +136,6 @@ func decodeFocusOp(d []byte, refs []interface{}) key.FocusOp {
|
||||
panic("invalid op")
|
||||
}
|
||||
return key.FocusOp{
|
||||
Focus: d[1] != 0,
|
||||
Tag: refs[0],
|
||||
}
|
||||
}
|
||||
|
||||
+11
-27
@@ -18,7 +18,7 @@ func TestKeyMultiples(t *testing.T) {
|
||||
|
||||
key.SoftKeyboardOp{Show: true}.Add(ops)
|
||||
key.InputOp{Tag: &handlers[0]}.Add(ops)
|
||||
key.FocusOp{Focus: true}.Add(ops)
|
||||
key.FocusOp{Tag: &handlers[2]}.Add(ops)
|
||||
key.InputOp{Tag: &handlers[1]}.Add(ops)
|
||||
|
||||
// The last one must be focused:
|
||||
@@ -40,27 +40,19 @@ func TestKeyStacked(t *testing.T) {
|
||||
|
||||
s := op.Save(ops)
|
||||
key.InputOp{Tag: &handlers[0]}.Add(ops)
|
||||
// FocusOp must not overwrite the
|
||||
// FocusOp{Focus: true}.
|
||||
key.FocusOp{Focus: false}.Add(ops)
|
||||
key.FocusOp{Tag: nil}.Add(ops)
|
||||
s.Load()
|
||||
s = op.Save(ops)
|
||||
key.SoftKeyboardOp{Show: false}.Add(ops)
|
||||
key.InputOp{Tag: &handlers[1]}.Add(ops)
|
||||
key.FocusOp{Focus: true}.Add(ops)
|
||||
key.FocusOp{Tag: &handlers[1]}.Add(ops)
|
||||
s.Load()
|
||||
s = op.Save(ops)
|
||||
key.InputOp{Tag: &handlers[2]}.Add(ops)
|
||||
// SoftwareKeyboardOp will open the keyboard,
|
||||
// overwriting `SoftKeyboardOp{Show: false}`.
|
||||
key.SoftKeyboardOp{Show: true}.Add(ops)
|
||||
s.Load()
|
||||
s = op.Save(ops)
|
||||
key.SoftKeyboardOp{Show: false}.Add(ops)
|
||||
key.InputOp{Tag: &handlers[3]}.Add(ops)
|
||||
// FocusOp must not overwrite the
|
||||
// FocusOp{Focus: true}.
|
||||
key.FocusOp{Focus: false}.Add(ops)
|
||||
s.Load()
|
||||
|
||||
r.Frame(ops)
|
||||
@@ -95,7 +87,7 @@ func TestKeyRemoveFocus(t *testing.T) {
|
||||
// New InputOp with Focus and Keyboard:
|
||||
s := op.Save(ops)
|
||||
key.InputOp{Tag: &handlers[0]}.Add(ops)
|
||||
key.FocusOp{Focus: true}.Add(ops)
|
||||
key.FocusOp{Tag: &handlers[0]}.Add(ops)
|
||||
key.SoftKeyboardOp{Show: true}.Add(ops)
|
||||
s.Load()
|
||||
|
||||
@@ -127,14 +119,13 @@ func TestKeyRemoveFocus(t *testing.T) {
|
||||
key.InputOp{Tag: &handlers[1]}.Add(ops)
|
||||
s.Load()
|
||||
|
||||
// Removing any Focus:
|
||||
// Remove focus by focusing on a tag that don't exist.
|
||||
s = op.Save(ops)
|
||||
key.FocusOp{Focus: false}.Add(ops)
|
||||
key.FocusOp{Tag: new(int)}.Add(ops)
|
||||
s.Load()
|
||||
|
||||
r.Frame(ops)
|
||||
|
||||
assertKeyEvent(t, r.Events(&handlers[0]), false)
|
||||
assertKeyEventUnexpected(t, r.Events(&handlers[1]))
|
||||
assertFocus(t, r, nil)
|
||||
assertKeyboard(t, r, TextInputClose)
|
||||
@@ -145,11 +136,6 @@ func TestKeyRemoveFocus(t *testing.T) {
|
||||
key.InputOp{Tag: &handlers[0]}.Add(ops)
|
||||
s.Load()
|
||||
|
||||
// Setting Focus without InputOp:
|
||||
s = op.Save(ops)
|
||||
key.FocusOp{Focus: true}.Add(ops)
|
||||
s.Load()
|
||||
|
||||
s = op.Save(ops)
|
||||
key.InputOp{Tag: &handlers[1]}.Add(ops)
|
||||
s.Load()
|
||||
@@ -166,23 +152,21 @@ func TestKeyRemoveFocus(t *testing.T) {
|
||||
// Set focus to InputOp which already
|
||||
// exists in the previous frame:
|
||||
s = op.Save(ops)
|
||||
key.FocusOp{Focus: true}.Add(ops)
|
||||
key.FocusOp{Tag: &handlers[0]}.Add(ops)
|
||||
key.InputOp{Tag: &handlers[0]}.Add(ops)
|
||||
key.SoftKeyboardOp{Show: true}.Add(ops)
|
||||
s.Load()
|
||||
|
||||
// Tries to remove focus:
|
||||
// It must not overwrite the previous `FocusOp`.
|
||||
// Remove focus.
|
||||
s = op.Save(ops)
|
||||
key.InputOp{Tag: &handlers[1]}.Add(ops)
|
||||
key.FocusOp{Focus: false}.Add(ops)
|
||||
key.FocusOp{Tag: nil}.Add(ops)
|
||||
s.Load()
|
||||
|
||||
r.Frame(ops)
|
||||
|
||||
assertKeyEvent(t, r.Events(&handlers[0]), true)
|
||||
assertKeyEventUnexpected(t, r.Events(&handlers[1]))
|
||||
assertFocus(t, r, &handlers[0])
|
||||
assertFocus(t, r, nil)
|
||||
assertKeyboard(t, r, TextInputOpen)
|
||||
}
|
||||
|
||||
@@ -193,7 +177,7 @@ func TestKeyFocusedInvisible(t *testing.T) {
|
||||
|
||||
// Set new InputOp with focus:
|
||||
s := op.Save(ops)
|
||||
key.FocusOp{Focus: true}.Add(ops)
|
||||
key.FocusOp{Tag: &handlers[0]}.Add(ops)
|
||||
key.InputOp{Tag: &handlers[0]}.Add(ops)
|
||||
key.SoftKeyboardOp{Show: true}.Add(ops)
|
||||
s.Load()
|
||||
|
||||
Reference in New Issue
Block a user