mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-06 01:45:36 +00:00
op: implement StackOp in terms of general save/load of state
Push/Pop only allows saving and restoring operation state in a stack-like manner. We're going to need restoring arbitrary state for implementing deferred operations. Generalize state save/restore and implement Push and Pop on top of that. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+42
-27
@@ -17,6 +17,14 @@ 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 {
|
||||
@@ -56,9 +64,9 @@ func (q *keyQueue) Frame(root *op.Ops, events *handlerEvents) {
|
||||
}
|
||||
q.reader.Reset(root)
|
||||
|
||||
focus, pri, keyboard := q.resolveFocus(events)
|
||||
if pri == priNone {
|
||||
focus = nil
|
||||
state := q.resolveFocus(events)
|
||||
if state.pri == priNone {
|
||||
state.tag = nil
|
||||
}
|
||||
for k, h := range q.handlers {
|
||||
if !h.visible {
|
||||
@@ -66,26 +74,26 @@ func (q *keyQueue) Frame(root *op.Ops, events *handlerEvents) {
|
||||
if q.focus == k {
|
||||
// Remove the focus from the handler that is no longer visible.
|
||||
q.focus = nil
|
||||
keyboard = TextInputClose
|
||||
state.keyboard = TextInputClose
|
||||
}
|
||||
}
|
||||
if h.new && k != focus {
|
||||
if h.new && k != state.tag {
|
||||
// Reset the handler on (each) first appearance.
|
||||
events.Add(k, key.FocusEvent{Focus: false})
|
||||
}
|
||||
}
|
||||
if focus != q.focus {
|
||||
if state.tag != q.focus {
|
||||
if q.focus != nil {
|
||||
events.Add(q.focus, key.FocusEvent{Focus: false})
|
||||
}
|
||||
q.focus = focus
|
||||
q.focus = state.tag
|
||||
if q.focus != nil {
|
||||
events.Add(q.focus, key.FocusEvent{Focus: true})
|
||||
} else {
|
||||
keyboard = TextInputClose
|
||||
state.keyboard = TextInputClose
|
||||
}
|
||||
}
|
||||
q.state = keyboard
|
||||
q.state = state.keyboard
|
||||
}
|
||||
|
||||
func (q *keyQueue) Push(e event.Event, events *handlerEvents) {
|
||||
@@ -94,28 +102,28 @@ func (q *keyQueue) Push(e event.Event, events *handlerEvents) {
|
||||
}
|
||||
}
|
||||
|
||||
func (q *keyQueue) resolveFocus(events *handlerEvents) (tag event.Tag, pri listenerPriority, keyboard TextInputState) {
|
||||
loop:
|
||||
func (q *keyQueue) resolveFocus(events *handlerEvents) resolveState {
|
||||
var state resolveState
|
||||
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 {
|
||||
pri = priNewFocus
|
||||
state.pri = priNewFocus
|
||||
} else {
|
||||
pri, keyboard = priNone, TextInputClose
|
||||
state.pri, state.keyboard = priNone, TextInputClose
|
||||
}
|
||||
case opconst.TypeKeySoftKeyboard:
|
||||
op := decodeSoftKeyboardOp(encOp.Data, encOp.Refs)
|
||||
if op.Show {
|
||||
keyboard = TextInputOpen
|
||||
state.keyboard = TextInputOpen
|
||||
} else {
|
||||
keyboard = TextInputClose
|
||||
state.keyboard = TextInputClose
|
||||
}
|
||||
case opconst.TypeKeyInput:
|
||||
op := decodeKeyInputOp(encOp.Data, encOp.Refs)
|
||||
if op.Tag == q.focus && pri < priCurrentFocus {
|
||||
pri = priCurrentFocus
|
||||
if op.Tag == q.focus && state.pri < priCurrentFocus {
|
||||
state.pri = priCurrentFocus
|
||||
}
|
||||
h, ok := q.handlers[op.Tag]
|
||||
if !ok {
|
||||
@@ -123,20 +131,27 @@ loop:
|
||||
q.handlers[op.Tag] = h
|
||||
}
|
||||
h.visible = true
|
||||
tag = op.Tag
|
||||
case opconst.TypePush:
|
||||
newK, newPri, newKeyboard := q.resolveFocus(events)
|
||||
if newKeyboard > keyboard {
|
||||
keyboard = newKeyboard
|
||||
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)...)
|
||||
}
|
||||
if newPri.replaces(pri) {
|
||||
tag, pri = newK, newPri
|
||||
q.states[id] = state
|
||||
state = resolveState{}
|
||||
case opconst.TypeLoad:
|
||||
id := ops.DecodeLoad(encOp.Data)
|
||||
restored := q.states[id]
|
||||
if state.keyboard > restored.keyboard {
|
||||
restored.keyboard = state.keyboard
|
||||
}
|
||||
case opconst.TypePop:
|
||||
break loop
|
||||
if state.pri.replaces(restored.pri) {
|
||||
restored.tag, restored.pri = state.tag, state.pri
|
||||
}
|
||||
state = restored
|
||||
}
|
||||
}
|
||||
return tag, pri, keyboard
|
||||
return state
|
||||
}
|
||||
|
||||
func (p listenerPriority) replaces(p2 listenerPriority) bool {
|
||||
|
||||
+38
-19
@@ -23,6 +23,8 @@ type pointerQueue struct {
|
||||
pointers []pointerInfo
|
||||
reader ops.Reader
|
||||
|
||||
// states holds the storage for save/restore ops.
|
||||
states []collectState
|
||||
scratch []event.Tag
|
||||
}
|
||||
|
||||
@@ -70,34 +72,51 @@ type areaNode struct {
|
||||
|
||||
type areaKind uint8
|
||||
|
||||
// collectState represents the state for collectHandlers
|
||||
type collectState struct {
|
||||
t f32.Affine2D
|
||||
area int
|
||||
node int
|
||||
pass bool
|
||||
}
|
||||
|
||||
const (
|
||||
areaRect areaKind = iota
|
||||
areaEllipse
|
||||
)
|
||||
|
||||
func (q *pointerQueue) collectHandlers(r *ops.Reader, events *handlerEvents, t f32.Affine2D, area, node int, pass bool) {
|
||||
func (q *pointerQueue) collectHandlers(r *ops.Reader, events *handlerEvents) {
|
||||
state := collectState{
|
||||
area: -1,
|
||||
node: -1,
|
||||
}
|
||||
for encOp, ok := r.Decode(); ok; encOp, ok = r.Decode() {
|
||||
switch opconst.OpType(encOp.Data[0]) {
|
||||
case opconst.TypePush:
|
||||
q.collectHandlers(r, events, t, area, node, pass)
|
||||
case opconst.TypePop:
|
||||
return
|
||||
case opconst.TypeSave:
|
||||
id := ops.DecodeSave(encOp.Data)
|
||||
if extra := id - len(q.states) + 1; extra > 0 {
|
||||
q.states = append(q.states, make([]collectState, extra)...)
|
||||
}
|
||||
q.states[id] = state
|
||||
case opconst.TypeLoad:
|
||||
id := ops.DecodeLoad(encOp.Data)
|
||||
state = q.states[id]
|
||||
case opconst.TypePass:
|
||||
pass = encOp.Data[1] != 0
|
||||
state.pass = encOp.Data[1] != 0
|
||||
case opconst.TypeArea:
|
||||
var op areaOp
|
||||
op.Decode(encOp.Data)
|
||||
q.areas = append(q.areas, areaNode{trans: t, next: area, area: op})
|
||||
area = len(q.areas) - 1
|
||||
q.areas = append(q.areas, areaNode{trans: state.t, next: state.area, area: op})
|
||||
state.area = len(q.areas) - 1
|
||||
q.hitTree = append(q.hitTree, hitNode{
|
||||
next: node,
|
||||
area: area,
|
||||
pass: pass,
|
||||
next: state.node,
|
||||
area: state.area,
|
||||
pass: state.pass,
|
||||
})
|
||||
node = len(q.hitTree) - 1
|
||||
state.node = len(q.hitTree) - 1
|
||||
case opconst.TypeTransform:
|
||||
dop := ops.DecodeTransform(encOp.Data)
|
||||
t = t.Mul(dop)
|
||||
state.t = state.t.Mul(dop)
|
||||
case opconst.TypePointerInput:
|
||||
op := pointer.InputOp{
|
||||
Tag: encOp.Refs[0].(event.Tag),
|
||||
@@ -105,12 +124,12 @@ func (q *pointerQueue) collectHandlers(r *ops.Reader, events *handlerEvents, t f
|
||||
Types: pointer.Type(encOp.Data[2]),
|
||||
}
|
||||
q.hitTree = append(q.hitTree, hitNode{
|
||||
next: node,
|
||||
area: area,
|
||||
pass: pass,
|
||||
next: state.node,
|
||||
area: state.area,
|
||||
pass: state.pass,
|
||||
tag: op.Tag,
|
||||
})
|
||||
node = len(q.hitTree) - 1
|
||||
state.node = len(q.hitTree) - 1
|
||||
h, ok := q.handlers[op.Tag]
|
||||
if !ok {
|
||||
h = new(pointerHandler)
|
||||
@@ -118,7 +137,7 @@ func (q *pointerQueue) collectHandlers(r *ops.Reader, events *handlerEvents, t f
|
||||
events.Add(op.Tag, pointer.Event{Type: pointer.Cancel})
|
||||
}
|
||||
h.active = true
|
||||
h.area = area
|
||||
h.area = state.area
|
||||
h.wantsGrab = h.wantsGrab || op.Grab
|
||||
h.types = h.types | op.Types
|
||||
case opconst.TypeCursor:
|
||||
@@ -192,7 +211,7 @@ func (q *pointerQueue) Frame(root *op.Ops, events *handlerEvents) {
|
||||
q.areas = q.areas[:0]
|
||||
q.cursors = q.cursors[:0]
|
||||
q.reader.Reset(root)
|
||||
q.collectHandlers(&q.reader, events, f32.Affine2D{}, -1, -1, false)
|
||||
q.collectHandlers(&q.reader, events)
|
||||
for k, h := range q.handlers {
|
||||
if !h.active {
|
||||
q.dropHandlers(events, k)
|
||||
|
||||
Reference in New Issue
Block a user