mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-06 18:05:35 +00:00
op: change Defer to only restore transformation state
It turns out restoring all operation state from the moment Defer is executed is too much; for example, a right-click pop-up needs the transformation, but not the current clip. Change Defer to only restore the transformation, and reset all other state. Other combinations may be needed in future; we'll deal with them then, possibly by exposing the load state mask. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+17
-6
@@ -869,6 +869,13 @@ func splitTransform(t f32.Affine2D) (srs f32.Affine2D, offset f32.Point) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *drawOps) save(id int, state drawState) {
|
||||||
|
if extra := id - len(d.states) + 1; extra > 0 {
|
||||||
|
d.states = append(d.states, make([]drawState, extra)...)
|
||||||
|
}
|
||||||
|
d.states[id] = state
|
||||||
|
}
|
||||||
|
|
||||||
func (d *drawOps) collectOps(r *ops.Reader, state drawState) {
|
func (d *drawOps) collectOps(r *ops.Reader, state drawState) {
|
||||||
var (
|
var (
|
||||||
quads quadsOp
|
quads quadsOp
|
||||||
@@ -876,6 +883,7 @@ func (d *drawOps) collectOps(r *ops.Reader, state drawState) {
|
|||||||
dashes dashOp
|
dashes dashOp
|
||||||
z int
|
z int
|
||||||
)
|
)
|
||||||
|
d.save(opconst.InitialStateID, state)
|
||||||
loop:
|
loop:
|
||||||
for encOp, ok := r.Decode(); ok; encOp, ok = r.Decode() {
|
for encOp, ok := r.Decode(); ok; encOp, ok = r.Decode() {
|
||||||
switch opconst.OpType(encOp.Data[0]) {
|
switch opconst.OpType(encOp.Data[0]) {
|
||||||
@@ -1029,13 +1037,16 @@ loop:
|
|||||||
}
|
}
|
||||||
case opconst.TypeSave:
|
case opconst.TypeSave:
|
||||||
id := ops.DecodeSave(encOp.Data)
|
id := ops.DecodeSave(encOp.Data)
|
||||||
if extra := id - len(d.states) + 1; extra > 0 {
|
d.save(id, state)
|
||||||
d.states = append(d.states, make([]drawState, extra)...)
|
|
||||||
}
|
|
||||||
d.states[id] = state
|
|
||||||
case opconst.TypeLoad:
|
case opconst.TypeLoad:
|
||||||
id := ops.DecodeLoad(encOp.Data)
|
id, mask := ops.DecodeLoad(encOp.Data)
|
||||||
state = d.states[id]
|
s := d.states[id]
|
||||||
|
if mask&opconst.TransformState != 0 {
|
||||||
|
state.t = s.t
|
||||||
|
}
|
||||||
|
if mask&^opconst.TransformState != 0 {
|
||||||
|
state = s
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-1
@@ -57,7 +57,7 @@ const (
|
|||||||
TypeKeyFocusLen = 1 + 1
|
TypeKeyFocusLen = 1 + 1
|
||||||
TypeKeySoftKeyboardLen = 1 + 1
|
TypeKeySoftKeyboardLen = 1 + 1
|
||||||
TypeSaveLen = 1 + 4
|
TypeSaveLen = 1 + 4
|
||||||
TypeLoadLen = 1 + 4
|
TypeLoadLen = 1 + 1 + 4
|
||||||
TypeAuxLen = 1
|
TypeAuxLen = 1
|
||||||
TypeClipLen = 1 + 4*4 + 1
|
TypeClipLen = 1 + 4*4 + 1
|
||||||
TypeProfileLen = 1
|
TypeProfileLen = 1
|
||||||
@@ -67,6 +67,20 @@ const (
|
|||||||
TypeDashLen = 1 + 4 + 1
|
TypeDashLen = 1 + 4 + 1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// StateMask is a bitmask of state types a load operation
|
||||||
|
// should restore.
|
||||||
|
type StateMask uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
TransformState StateMask = 1 << iota
|
||||||
|
|
||||||
|
AllState = ^StateMask(0)
|
||||||
|
)
|
||||||
|
|
||||||
|
// InitialStateID is the ID for saving and loading
|
||||||
|
// the initial operation state.
|
||||||
|
const InitialStateID = 0
|
||||||
|
|
||||||
func (t OpType) Size() int {
|
func (t OpType) Size() int {
|
||||||
return [...]int{
|
return [...]int{
|
||||||
TypeMacroLen,
|
TypeMacroLen,
|
||||||
|
|||||||
+3
-3
@@ -72,11 +72,11 @@ func DecodeSave(data []byte) int {
|
|||||||
return int(bo.Uint32(data[1:]))
|
return int(bo.Uint32(data[1:]))
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeLoad decodes the state id of a restore op.
|
// DecodeLoad decodes the state id and mask of a load op.
|
||||||
func DecodeLoad(data []byte) int {
|
func DecodeLoad(data []byte) (int, opconst.StateMask) {
|
||||||
if opconst.OpType(data[0]) != opconst.TypeLoad {
|
if opconst.OpType(data[0]) != opconst.TypeLoad {
|
||||||
panic("invalid op")
|
panic("invalid op")
|
||||||
}
|
}
|
||||||
bo := binary.LittleEndian
|
bo := binary.LittleEndian
|
||||||
return int(bo.Uint32(data[1:]))
|
return int(bo.Uint32(data[2:])), opconst.StateMask(data[1])
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 396 B After Width: | Height: | Size: 420 B |
@@ -107,13 +107,17 @@ func TestDeferredPaint(t *testing.T) {
|
|||||||
run(t, func(o *op.Ops) {
|
run(t, func(o *op.Ops) {
|
||||||
state := op.Save(o)
|
state := op.Save(o)
|
||||||
clip.Rect(image.Rect(0, 0, 80, 80)).Op().Add(o)
|
clip.Rect(image.Rect(0, 0, 80, 80)).Op().Add(o)
|
||||||
paint.ColorOp{Color: color.NRGBA{A: 0xff, R: 0xff}}.Add(o)
|
paint.ColorOp{Color: color.NRGBA{A: 0xff, G: 0xff}}.Add(o)
|
||||||
|
paint.PaintOp{}.Add(o)
|
||||||
|
|
||||||
|
op.Affine(f32.Affine2D{}.Offset(f32.Pt(20, 20))).Add(o)
|
||||||
m := op.Record(o)
|
m := op.Record(o)
|
||||||
|
clip.Rect(image.Rect(0, 0, 80, 80)).Op().Add(o)
|
||||||
|
paint.ColorOp{Color: color.NRGBA{A: 0xff, R: 0xff, G: 0xff}}.Add(o)
|
||||||
paint.PaintOp{}.Add(o)
|
paint.PaintOp{}.Add(o)
|
||||||
paintMacro := m.Stop()
|
paintMacro := m.Stop()
|
||||||
op.Defer(o, paintMacro)
|
op.Defer(o, paintMacro)
|
||||||
paint.ColorOp{Color: color.NRGBA{A: 0xff, G: 0xff}}.Add(o)
|
|
||||||
paint.PaintOp{}.Add(o)
|
|
||||||
state.Load()
|
state.Load()
|
||||||
op.Affine(f32.Affine2D{}.Offset(f32.Pt(10, 10))).Add(o)
|
op.Affine(f32.Affine2D{}.Offset(f32.Pt(10, 10))).Add(o)
|
||||||
clip.Rect(image.Rect(0, 0, 80, 80)).Op().Add(o)
|
clip.Rect(image.Rect(0, 0, 80, 80)).Op().Add(o)
|
||||||
|
|||||||
+4
-2
@@ -140,7 +140,7 @@ func (q *keyQueue) resolveFocus(events *handlerEvents) resolveState {
|
|||||||
q.states[id] = state
|
q.states[id] = state
|
||||||
state = resolveState{}
|
state = resolveState{}
|
||||||
case opconst.TypeLoad:
|
case opconst.TypeLoad:
|
||||||
id := ops.DecodeLoad(encOp.Data)
|
id, mask := ops.DecodeLoad(encOp.Data)
|
||||||
restored := q.states[id]
|
restored := q.states[id]
|
||||||
if state.keyboard > restored.keyboard {
|
if state.keyboard > restored.keyboard {
|
||||||
restored.keyboard = state.keyboard
|
restored.keyboard = state.keyboard
|
||||||
@@ -148,7 +148,9 @@ func (q *keyQueue) resolveFocus(events *handlerEvents) resolveState {
|
|||||||
if state.pri.replaces(restored.pri) {
|
if state.pri.replaces(restored.pri) {
|
||||||
restored.tag, restored.pri = state.tag, state.pri
|
restored.tag, restored.pri = state.tag, state.pri
|
||||||
}
|
}
|
||||||
state = restored
|
if mask != 0 {
|
||||||
|
state = restored
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return state
|
return state
|
||||||
|
|||||||
+17
-6
@@ -85,22 +85,33 @@ const (
|
|||||||
areaEllipse
|
areaEllipse
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (q *pointerQueue) save(id int, state collectState) {
|
||||||
|
if extra := id - len(q.states) + 1; extra > 0 {
|
||||||
|
q.states = append(q.states, make([]collectState, extra)...)
|
||||||
|
}
|
||||||
|
q.states[id] = state
|
||||||
|
}
|
||||||
|
|
||||||
func (q *pointerQueue) collectHandlers(r *ops.Reader, events *handlerEvents) {
|
func (q *pointerQueue) collectHandlers(r *ops.Reader, events *handlerEvents) {
|
||||||
state := collectState{
|
state := collectState{
|
||||||
area: -1,
|
area: -1,
|
||||||
node: -1,
|
node: -1,
|
||||||
}
|
}
|
||||||
|
q.save(opconst.InitialStateID, state)
|
||||||
for encOp, ok := r.Decode(); ok; encOp, ok = r.Decode() {
|
for encOp, ok := r.Decode(); ok; encOp, ok = r.Decode() {
|
||||||
switch opconst.OpType(encOp.Data[0]) {
|
switch opconst.OpType(encOp.Data[0]) {
|
||||||
case opconst.TypeSave:
|
case opconst.TypeSave:
|
||||||
id := ops.DecodeSave(encOp.Data)
|
id := ops.DecodeSave(encOp.Data)
|
||||||
if extra := id - len(q.states) + 1; extra > 0 {
|
q.save(id, state)
|
||||||
q.states = append(q.states, make([]collectState, extra)...)
|
|
||||||
}
|
|
||||||
q.states[id] = state
|
|
||||||
case opconst.TypeLoad:
|
case opconst.TypeLoad:
|
||||||
id := ops.DecodeLoad(encOp.Data)
|
id, mask := ops.DecodeLoad(encOp.Data)
|
||||||
state = q.states[id]
|
s := q.states[id]
|
||||||
|
if mask&opconst.TransformState != 0 {
|
||||||
|
state.t = s.t
|
||||||
|
}
|
||||||
|
if mask&^opconst.TransformState != 0 {
|
||||||
|
state = s
|
||||||
|
}
|
||||||
case opconst.TypePass:
|
case opconst.TypePass:
|
||||||
state.pass = encOp.Data[1] != 0
|
state.pass = encOp.Data[1] != 0
|
||||||
case opconst.TypeArea:
|
case opconst.TypeArea:
|
||||||
|
|||||||
@@ -144,8 +144,8 @@ type pc struct {
|
|||||||
|
|
||||||
// Defer executes c after all other operations have completed,
|
// Defer executes c after all other operations have completed,
|
||||||
// including previously deferred operations.
|
// including previously deferred operations.
|
||||||
// Defer saves the operation state, which is then loaded prior
|
// Defer saves the current transformation and restores it prior
|
||||||
// to execution.
|
// to execution. All other operation state is reset.
|
||||||
//
|
//
|
||||||
// Note that deferred operations are executed in first-in-first-out
|
// Note that deferred operations are executed in first-in-first-out
|
||||||
// order, unlike the Go facility of the same name.
|
// order, unlike the Go facility of the same name.
|
||||||
@@ -156,8 +156,8 @@ func Defer(o *Ops, c CallOp) {
|
|||||||
state := Save(o)
|
state := Save(o)
|
||||||
// Wrap c in a macro that loads the saved state before execution.
|
// Wrap c in a macro that loads the saved state before execution.
|
||||||
m := Record(o)
|
m := Record(o)
|
||||||
//
|
load(o, opconst.InitialStateID, opconst.AllState)
|
||||||
state.load()
|
load(o, state.id, opconst.TransformState)
|
||||||
c.Add(o)
|
c.Add(o)
|
||||||
c = m.Stop()
|
c = m.Stop()
|
||||||
// A Defer is recorded as a TypeDefer followed by the
|
// A Defer is recorded as a TypeDefer followed by the
|
||||||
@@ -175,11 +175,17 @@ func Save(o *Ops) StateOp {
|
|||||||
id: o.nextStateID,
|
id: o.nextStateID,
|
||||||
macroID: o.macroStack.currentID,
|
macroID: o.macroStack.currentID,
|
||||||
}
|
}
|
||||||
|
save(o, s.id)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// save records a save of the operations state to
|
||||||
|
// id.
|
||||||
|
func save(o *Ops, id int) {
|
||||||
bo := binary.LittleEndian
|
bo := binary.LittleEndian
|
||||||
data := o.Write(opconst.TypeSaveLen)
|
data := o.Write(opconst.TypeSaveLen)
|
||||||
data[0] = byte(opconst.TypeSave)
|
data[0] = byte(opconst.TypeSave)
|
||||||
bo.PutUint32(data[1:], uint32(s.id))
|
bo.PutUint32(data[1:], uint32(id))
|
||||||
return s
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load a previously saved operations state.
|
// Load a previously saved operations state.
|
||||||
@@ -187,18 +193,20 @@ func (s StateOp) Load() {
|
|||||||
if s.ops.macroStack.currentID != s.macroID {
|
if s.ops.macroStack.currentID != s.macroID {
|
||||||
panic("load in a different macro than save")
|
panic("load in a different macro than save")
|
||||||
}
|
}
|
||||||
s.load()
|
|
||||||
}
|
|
||||||
|
|
||||||
// load is like Load without the same-macro check.
|
|
||||||
func (s StateOp) load() {
|
|
||||||
if s.id == 0 {
|
if s.id == 0 {
|
||||||
panic("zero-value op")
|
panic("zero-value op")
|
||||||
}
|
}
|
||||||
|
load(s.ops, s.id, opconst.AllState)
|
||||||
|
}
|
||||||
|
|
||||||
|
// load a previously saved operations state given
|
||||||
|
// its ID. Only state included in mask is affected.
|
||||||
|
func load(o *Ops, id int, m opconst.StateMask) {
|
||||||
bo := binary.LittleEndian
|
bo := binary.LittleEndian
|
||||||
data := s.ops.Write(opconst.TypeLoadLen)
|
data := o.Write(opconst.TypeLoadLen)
|
||||||
data[0] = byte(opconst.TypeLoad)
|
data[0] = byte(opconst.TypeLoad)
|
||||||
bo.PutUint32(data[1:], uint32(s.id))
|
data[1] = byte(m)
|
||||||
|
bo.PutUint32(data[2:], uint32(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the Ops, preparing it for re-use. Reset invalidates
|
// Reset the Ops, preparing it for re-use. Reset invalidates
|
||||||
|
|||||||
Reference in New Issue
Block a user