mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-06 01:45:36 +00:00
io/input: implement lazy event routing
This change defers event routing from the time the event is queued until the time Events is called. This allows a future change to execute commands immediately and to react to event order changes during a frame. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+62
-56
@@ -24,13 +24,17 @@ type EditorState struct {
|
||||
type TextInputState uint8
|
||||
|
||||
type keyQueue struct {
|
||||
focus event.Tag
|
||||
order []event.Tag
|
||||
dirOrder []dirFocusEntry
|
||||
handlers map[event.Tag]*keyHandler
|
||||
state TextInputState
|
||||
hint key.InputHint
|
||||
content EditorState
|
||||
}
|
||||
|
||||
// keyState is the input state related to key events.
|
||||
type keyState struct {
|
||||
focus event.Tag
|
||||
state TextInputState
|
||||
content EditorState
|
||||
}
|
||||
|
||||
type keyHandler struct {
|
||||
@@ -67,21 +71,18 @@ func (q *keyQueue) inputHint(op key.InputHintOp) {
|
||||
h.hint = op.Hint
|
||||
}
|
||||
|
||||
// InputState returns the last text input state as
|
||||
// determined in Frame.
|
||||
func (q *keyQueue) InputState() TextInputState {
|
||||
state := q.state
|
||||
q.state = TextInputKeep
|
||||
return state
|
||||
// InputState returns the input state and returns a state
|
||||
// reset to [TextInputKeep].
|
||||
func (s keyState) InputState() (keyState, TextInputState) {
|
||||
state := s.state
|
||||
s.state = TextInputKeep
|
||||
return s, state
|
||||
}
|
||||
|
||||
// InputHint returns the input hint from the focused handler and whether it was
|
||||
// changed since the last call.
|
||||
func (q *keyQueue) InputHint() (key.InputHint, bool) {
|
||||
if q.focus == nil {
|
||||
return q.hint, false
|
||||
}
|
||||
focused, ok := q.handlers[q.focus]
|
||||
func (q *keyQueue) InputHint(state keyState) (key.InputHint, bool) {
|
||||
focused, ok := q.handlers[state.focus]
|
||||
if !ok {
|
||||
return q.hint, false
|
||||
}
|
||||
@@ -108,13 +109,13 @@ func (q *keyQueue) ResetEvent(k event.Tag) (event.Event, bool) {
|
||||
return key.FocusEvent{Focus: false}, true
|
||||
}
|
||||
|
||||
func (q *keyQueue) Frame() {
|
||||
func (q *keyQueue) Frame(state keyState) keyState {
|
||||
for k, h := range q.handlers {
|
||||
if !h.visible || !h.focusable {
|
||||
if q.focus == k {
|
||||
if state.focus == k {
|
||||
// Remove focus from the handler that is no longer focusable.
|
||||
q.focus = nil
|
||||
q.state = TextInputClose
|
||||
state.focus = nil
|
||||
state.state = TextInputClose
|
||||
}
|
||||
if !h.visible && !h.focusable {
|
||||
delete(q.handlers, k)
|
||||
@@ -126,6 +127,7 @@ func (q *keyQueue) Frame() {
|
||||
h.active = false
|
||||
}
|
||||
q.updateFocusLayout()
|
||||
return state
|
||||
}
|
||||
|
||||
// updateFocusLayout partitions input handlers handlers into rows
|
||||
@@ -168,13 +170,13 @@ func (q *keyQueue) updateFocusLayout() {
|
||||
}
|
||||
|
||||
// MoveFocus attempts to move the focus in the direction of dir.
|
||||
func (q *keyQueue) MoveFocus(evts []taggedEvent, dir key.FocusDirection) []taggedEvent {
|
||||
func (q *keyQueue) MoveFocus(state keyState, dir key.FocusDirection) (keyState, []taggedEvent) {
|
||||
if len(q.dirOrder) == 0 {
|
||||
return nil
|
||||
return state, nil
|
||||
}
|
||||
order := 0
|
||||
if q.focus != nil {
|
||||
order = q.handlers[q.focus].dirOrder
|
||||
if state.focus != nil {
|
||||
order = q.handlers[state.focus].dirOrder
|
||||
}
|
||||
focus := q.dirOrder[order]
|
||||
switch dir {
|
||||
@@ -186,8 +188,8 @@ func (q *keyQueue) MoveFocus(evts []taggedEvent, dir key.FocusDirection) []tagge
|
||||
if dir == key.FocusBackward {
|
||||
order = -1
|
||||
}
|
||||
if q.focus != nil {
|
||||
order = q.handlers[q.focus].order
|
||||
if state.focus != nil {
|
||||
order = q.handlers[state.focus].order
|
||||
if dir == key.FocusForward {
|
||||
order++
|
||||
} else {
|
||||
@@ -195,10 +197,10 @@ func (q *keyQueue) MoveFocus(evts []taggedEvent, dir key.FocusDirection) []tagge
|
||||
}
|
||||
}
|
||||
order = (order + len(q.order)) % len(q.order)
|
||||
return q.Focus(evts, q.order[order])
|
||||
return q.Focus(state, q.order[order])
|
||||
case key.FocusRight, key.FocusLeft:
|
||||
next := order
|
||||
if q.focus != nil {
|
||||
if state.focus != nil {
|
||||
next = order + 1
|
||||
if dir == key.FocusLeft {
|
||||
next = order - 1
|
||||
@@ -207,7 +209,7 @@ func (q *keyQueue) MoveFocus(evts []taggedEvent, dir key.FocusDirection) []tagge
|
||||
if 0 <= next && next < len(q.dirOrder) {
|
||||
newFocus := q.dirOrder[next]
|
||||
if newFocus.row == focus.row {
|
||||
return q.Focus(evts, newFocus.tag)
|
||||
return q.Focus(state, newFocus.tag)
|
||||
}
|
||||
}
|
||||
case key.FocusUp, key.FocusDown:
|
||||
@@ -216,7 +218,7 @@ func (q *keyQueue) MoveFocus(evts []taggedEvent, dir key.FocusDirection) []tagge
|
||||
delta = -1
|
||||
}
|
||||
nextRow := 0
|
||||
if q.focus != nil {
|
||||
if state.focus != nil {
|
||||
nextRow = focus.row + delta
|
||||
}
|
||||
var closest event.Tag
|
||||
@@ -243,10 +245,10 @@ func (q *keyQueue) MoveFocus(evts []taggedEvent, dir key.FocusDirection) []tagge
|
||||
order += delta
|
||||
}
|
||||
if closest != nil {
|
||||
return q.Focus(evts, closest)
|
||||
return q.Focus(state, closest)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return state, nil
|
||||
}
|
||||
|
||||
func (q *keyQueue) BoundsFor(t event.Tag) image.Rectangle {
|
||||
@@ -281,35 +283,37 @@ func keyFilterMatch(f key.Filter, e key.Event) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (q *keyQueue) Focus(evts []taggedEvent, focus event.Tag) []taggedEvent {
|
||||
func (q *keyQueue) Focus(state keyState, focus event.Tag) (keyState, []taggedEvent) {
|
||||
if focus != nil {
|
||||
if _, exists := q.handlers[focus]; !exists {
|
||||
focus = nil
|
||||
}
|
||||
}
|
||||
if focus == q.focus {
|
||||
return evts
|
||||
if focus == state.focus {
|
||||
return state, nil
|
||||
}
|
||||
q.content = EditorState{}
|
||||
if q.focus != nil {
|
||||
evts = append(evts, taggedEvent{tag: q.focus, event: key.FocusEvent{Focus: false}})
|
||||
state.content = EditorState{}
|
||||
var evts []taggedEvent
|
||||
if state.focus != nil {
|
||||
evts = append(evts, taggedEvent{tag: state.focus, event: key.FocusEvent{Focus: false}})
|
||||
}
|
||||
q.focus = focus
|
||||
if q.focus != nil {
|
||||
evts = append(evts, taggedEvent{tag: q.focus, event: key.FocusEvent{Focus: true}})
|
||||
state.focus = focus
|
||||
if state.focus != nil {
|
||||
evts = append(evts, taggedEvent{tag: state.focus, event: key.FocusEvent{Focus: true}})
|
||||
}
|
||||
if q.focus == nil || q.state == TextInputKeep {
|
||||
q.state = TextInputClose
|
||||
if state.focus == nil || state.state == TextInputKeep {
|
||||
state.state = TextInputClose
|
||||
}
|
||||
return evts
|
||||
return state, evts
|
||||
}
|
||||
|
||||
func (q *keyQueue) softKeyboard(show bool) {
|
||||
func (s keyState) softKeyboard(show bool) keyState {
|
||||
if show {
|
||||
q.state = TextInputOpen
|
||||
s.state = TextInputOpen
|
||||
} else {
|
||||
q.state = TextInputClose
|
||||
s.state = TextInputClose
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (q *keyQueue) filter(tag event.Tag, f key.Filter) {
|
||||
@@ -349,26 +353,28 @@ func (q *keyQueue) inputOp(tag event.Tag, t f32.Affine2D, area int, bounds image
|
||||
h.trans = t
|
||||
}
|
||||
|
||||
func (q *keyQueue) setSelection(req key.SelectionCmd) {
|
||||
if req.Tag != q.focus {
|
||||
return
|
||||
func (q *keyQueue) setSelection(state keyState, req key.SelectionCmd) keyState {
|
||||
if req.Tag != state.focus {
|
||||
return state
|
||||
}
|
||||
q.content.Selection.Range = req.Range
|
||||
q.content.Selection.Caret = req.Caret
|
||||
state.content.Selection.Range = req.Range
|
||||
state.content.Selection.Caret = req.Caret
|
||||
return state
|
||||
}
|
||||
|
||||
func (q *keyQueue) editorState() EditorState {
|
||||
s := q.content
|
||||
if f := q.focus; f != nil {
|
||||
func (q *keyQueue) editorState(state keyState) EditorState {
|
||||
s := state.content
|
||||
if f := state.focus; f != nil {
|
||||
s.Selection.Transform = q.handlers[f].trans
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (q *keyQueue) setSnippet(req key.SnippetCmd) {
|
||||
if req.Tag == q.focus {
|
||||
q.content.Snippet = req.Snippet
|
||||
func (q *keyQueue) setSnippet(state keyState, req key.SnippetCmd) keyState {
|
||||
if req.Tag == state.focus {
|
||||
state.content.Snippet = req.Snippet
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
func (t TextInputState) String() string {
|
||||
|
||||
Reference in New Issue
Block a user