aboutsummaryrefslogtreecommitdiff
path: root/vendor/gioui.org/op/op.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gioui.org/op/op.go')
-rw-r--r--vendor/gioui.org/op/op.go242
1 files changed, 88 insertions, 154 deletions
diff --git a/vendor/gioui.org/op/op.go b/vendor/gioui.org/op/op.go
index 174d318..f0b2620 100644
--- a/vendor/gioui.org/op/op.go
+++ b/vendor/gioui.org/op/op.go
@@ -30,25 +30,25 @@ Drawing a colored square:
State
-An Ops list can be viewed as a very simple virtual machine: it has an implicit
-mutable state stack and execution flow can be controlled with macros.
+An Ops list can be viewed as a very simple virtual machine: it has state such
+as transformation and color and execution flow can be controlled with macros.
-The StackOp saves the current state to the state stack and restores it later:
+Some state, such as the current color, is modified directly by operations with
+Add methods. Other state, such as transformation and clip shape, are
+represented by stacks.
+
+This example sets the simple color state and pushes an offset to the
+transformation stack.
ops := new(op.Ops)
- // Save the current state, in particular the transform.
- stack := op.Push(ops)
- // Apply a transform to subsequent operations.
- op.Offset(...).Add(ops)
+ // Set the color.
+ paint.ColorOp{...}.Add(ops)
+ // Apply an offset to subsequent operations.
+ stack := op.Offset(...).Push(ops)
...
- // Restore the previous transform.
+ // Undo the offset transformation.
stack.Pop()
-You can also use this one-line to save the current state and restore it at the
-end of a function :
-
- defer op.Push(ops).Pop()
-
The MacroOp records a list of operations to be executed later:
ops := new(op.Ops)
@@ -71,44 +71,29 @@ import (
"time"
"gioui.org/f32"
- "gioui.org/internal/opconst"
+ "gioui.org/internal/ops"
)
// Ops holds a list of operations. Operations are stored in
// serialized form to avoid garbage during construction of
// the ops list.
type Ops struct {
- // version is incremented at each Reset.
- version int
- // data contains the serialized operations.
- data []byte
- // External references for operations.
- refs []interface{}
-
- stackStack stack
- macroStack stack
-}
-
-// StackOp saves and restores the operation state
-// in a stack-like manner.
-type StackOp struct {
- id stackID
- macroID int
- ops *Ops
+ // Internal is for internal use, despite being exported.
+ Internal ops.Ops
}
// MacroOp records a list of operations for later use.
type MacroOp struct {
- ops *Ops
- id stackID
- pc pc
+ ops *ops.Ops
+ id ops.StackID
+ pc ops.PC
}
// CallOp invokes the operations recorded by Record.
type CallOp struct {
// Ops is the list of operations to invoke.
- ops *Ops
- pc pc
+ ops *ops.Ops
+ pc ops.PC
}
// InvalidateOp requests a redraw at the given time. Use
@@ -117,100 +102,58 @@ type InvalidateOp struct {
At time.Time
}
-// TransformOp applies a transform to the current transform. The zero value
-// for TransformOp represents the identity transform.
+// TransformOp represents a transformation that can be pushed on the
+// transformation stack.
type TransformOp struct {
t f32.Affine2D
}
-// stack tracks the integer identities of StackOp and MacroOp
-// operations to ensure correct pairing of Push/Pop and Record/End.
-type stack struct {
- currentID int
- nextID int
-}
-
-type stackID struct {
- id int
- prev int
-}
-
-type pc struct {
- data int
- refs int
-}
-
-// Push (save) the current operations state.
-func Push(o *Ops) StackOp {
- s := StackOp{
- ops: o,
- id: o.stackStack.push(),
- macroID: o.macroStack.currentID,
- }
- data := o.Write(opconst.TypePushLen)
- data[0] = byte(opconst.TypePush)
- return s
+// TransformStack represents a TransformOp pushed on the transformation stack.
+type TransformStack struct {
+ id ops.StackID
+ macroID int
+ ops *ops.Ops
}
-// Pop (restore) a previously Pushed operations state.
-func (s StackOp) Pop() {
- if s.ops.macroStack.currentID != s.macroID {
- panic("pop in a different macro than push")
+// Defer executes c after all other operations have completed, including
+// previously deferred operations.
+// Defer saves the transformation stack and pushes it prior to executing
+// c. All other operation state is reset.
+//
+// Note that deferred operations are executed in first-in-first-out order,
+// unlike the Go facility of the same name.
+func Defer(o *Ops, c CallOp) {
+ if c.ops == nil {
+ return
}
- s.ops.stackStack.pop(s.id)
- data := s.ops.Write(opconst.TypePopLen)
- data[0] = byte(opconst.TypePop)
+ state := ops.Save(&o.Internal)
+ // Wrap c in a macro that loads the saved state before execution.
+ m := Record(o)
+ state.Load()
+ c.Add(o)
+ c = m.Stop()
+ // A Defer is recorded as a TypeDefer followed by the
+ // wrapped macro.
+ data := ops.Write(&o.Internal, ops.TypeDeferLen)
+ data[0] = byte(ops.TypeDefer)
+ c.Add(o)
}
// Reset the Ops, preparing it for re-use. Reset invalidates
// any recorded macros.
func (o *Ops) Reset() {
- o.stackStack = stack{}
- o.macroStack = stack{}
- // Leave references to the GC.
- for i := range o.refs {
- o.refs[i] = nil
- }
- o.data = o.data[:0]
- o.refs = o.refs[:0]
- o.version++
-}
-
-// Data is for internal use only.
-func (o *Ops) Data() []byte {
- return o.data
-}
-
-// Refs is for internal use only.
-func (o *Ops) Refs() []interface{} {
- return o.refs
-}
-
-// Version is for internal use only.
-func (o *Ops) Version() int {
- return o.version
-}
-
-// Write is for internal use only.
-func (o *Ops) Write(n int, refs ...interface{}) []byte {
- o.data = append(o.data, make([]byte, n)...)
- o.refs = append(o.refs, refs...)
- return o.data[len(o.data)-n:]
-}
-
-func (o *Ops) pc() pc {
- return pc{data: len(o.data), refs: len(o.refs)}
+ ops.Reset(&o.Internal)
}
// Record a macro of operations.
func Record(o *Ops) MacroOp {
m := MacroOp{
- ops: o,
- id: o.macroStack.push(),
- pc: o.pc(),
+ ops: &o.Internal,
+ id: ops.PushMacro(&o.Internal),
+ pc: ops.PCFor(&o.Internal),
}
// Reserve room for a macro definition. Updated in Stop.
- m.ops.Write(opconst.TypeMacroLen)
+ ops.Write(m.ops, ops.TypeMacroLen)
m.fill()
return m
}
@@ -218,7 +161,7 @@ func Record(o *Ops) MacroOp {
// Stop ends a previously started recording and returns an
// operation for replaying it.
func (m MacroOp) Stop() CallOp {
- m.ops.macroStack.pop(m.id)
+ ops.PopMacro(m.ops, m.id)
m.fill()
return CallOp{
ops: m.ops,
@@ -227,14 +170,7 @@ func (m MacroOp) Stop() CallOp {
}
func (m MacroOp) fill() {
- pc := m.ops.pc()
- // Fill out the macro definition reserved in Record.
- data := m.ops.data[m.pc.data:]
- data = data[:opconst.TypeMacroLen]
- data[0] = byte(opconst.TypeMacro)
- bo := binary.LittleEndian
- bo.PutUint32(data[1:], uint32(pc.data))
- bo.PutUint32(data[5:], uint32(pc.refs))
+ ops.FillMacro(m.ops, m.pc)
}
// Add the recorded list of operations. Add
@@ -244,16 +180,12 @@ func (c CallOp) Add(o *Ops) {
if c.ops == nil {
return
}
- data := o.Write(opconst.TypeCallLen, c.ops)
- data[0] = byte(opconst.TypeCall)
- bo := binary.LittleEndian
- bo.PutUint32(data[1:], uint32(c.pc.data))
- bo.PutUint32(data[5:], uint32(c.pc.refs))
+ ops.AddCall(&o.Internal, c.ops, c.pc)
}
func (r InvalidateOp) Add(o *Ops) {
- data := o.Write(opconst.TypeRedrawLen)
- data[0] = byte(opconst.TypeInvalidate)
+ data := ops.Write(&o.Internal, ops.TypeRedrawLen)
+ data[0] = byte(ops.TypeInvalidate)
bo := binary.LittleEndian
// UnixNano cannot represent the zero time.
if t := r.At; !t.IsZero() {
@@ -274,36 +206,38 @@ func Affine(a f32.Affine2D) TransformOp {
return TransformOp{t: a}
}
-func (t TransformOp) Add(o *Ops) {
- data := o.Write(opconst.TypeTransformLen)
- data[0] = byte(opconst.TypeTransform)
- bo := binary.LittleEndian
- a, b, c, d, e, f := t.t.Elems()
- bo.PutUint32(data[1:], math.Float32bits(a))
- bo.PutUint32(data[1+4*1:], math.Float32bits(b))
- bo.PutUint32(data[1+4*2:], math.Float32bits(c))
- bo.PutUint32(data[1+4*3:], math.Float32bits(d))
- bo.PutUint32(data[1+4*4:], math.Float32bits(e))
- bo.PutUint32(data[1+4*5:], math.Float32bits(f))
+// Push the current transformation to the stack and then multiply the
+// current transformation with t.
+func (t TransformOp) Push(o *Ops) TransformStack {
+ id, macroID := ops.PushOp(&o.Internal, ops.TransStack)
+ t.add(o, true)
+ return TransformStack{ops: &o.Internal, id: id, macroID: macroID}
}
-func (s *stack) push() stackID {
- s.nextID++
- sid := stackID{
- id: s.nextID,
- prev: s.currentID,
- }
- s.currentID = s.nextID
- return sid
+// Add is like Push except it doesn't push the current transformation to the
+// stack.
+func (t TransformOp) Add(o *Ops) {
+ t.add(o, false)
}
-func (s *stack) check(sid stackID) {
- if s.currentID != sid.id {
- panic("unbalanced operation")
+func (t TransformOp) add(o *Ops, push bool) {
+ data := ops.Write(&o.Internal, ops.TypeTransformLen)
+ data[0] = byte(ops.TypeTransform)
+ if push {
+ data[1] = 1
}
-}
-
-func (s *stack) pop(sid stackID) {
- s.check(sid)
- s.currentID = sid.prev
+ bo := binary.LittleEndian
+ a, b, c, d, e, f := t.t.Elems()
+ bo.PutUint32(data[2:], math.Float32bits(a))
+ bo.PutUint32(data[2+4*1:], math.Float32bits(b))
+ bo.PutUint32(data[2+4*2:], math.Float32bits(c))
+ bo.PutUint32(data[2+4*3:], math.Float32bits(d))
+ bo.PutUint32(data[2+4*4:], math.Float32bits(e))
+ bo.PutUint32(data[2+4*5:], math.Float32bits(f))
+}
+
+func (t TransformStack) Pop() {
+ ops.PopOp(t.ops, ops.TransStack, t.id, t.macroID)
+ data := ops.Write(t.ops, ops.TypePopTransformLen)
+ data[0] = byte(ops.TypePopTransform)
}