gioui.org@v0.6.1-0.20240506124620-7a9ce51988ce/op/op.go (about) 1 // SPDX-License-Identifier: Unlicense OR MIT 2 3 /* 4 Package op implements operations for updating a user interface. 5 6 Gio programs use operations, or ops, for describing their user 7 interfaces. There are operations for drawing, defining input 8 handlers, changing window properties as well as operations for 9 controlling the execution of other operations. 10 11 Ops represents a list of operations. The most important use 12 for an Ops list is to describe a complete user interface update 13 to a ui/app.Window's Update method. 14 15 Drawing a colored square: 16 17 import "gioui.org/unit" 18 import "gioui.org/app" 19 import "gioui.org/op/paint" 20 21 var w app.Window 22 var e system.FrameEvent 23 ops := new(op.Ops) 24 ... 25 ops.Reset() 26 paint.ColorOp{Color: ...}.Add(ops) 27 paint.PaintOp{Rect: ...}.Add(ops) 28 e.Frame(ops) 29 30 # State 31 32 An Ops list can be viewed as a very simple virtual machine: it has state such 33 as transformation and color and execution flow can be controlled with macros. 34 35 Some state, such as the current color, is modified directly by operations with 36 Add methods. Other state, such as transformation and clip shape, are 37 represented by stacks. 38 39 This example sets the simple color state and pushes an offset to the 40 transformation stack. 41 42 ops := new(op.Ops) 43 // Set the color. 44 paint.ColorOp{...}.Add(ops) 45 // Apply an offset to subsequent operations. 46 stack := op.Offset(...).Push(ops) 47 ... 48 // Undo the offset transformation. 49 stack.Pop() 50 51 The MacroOp records a list of operations to be executed later: 52 53 ops := new(op.Ops) 54 macro := op.Record(ops) 55 // Record operations by adding them. 56 ... 57 // End recording. 58 call := macro.Stop() 59 60 // replay the recorded operations: 61 call.Add(ops) 62 */ 63 package op 64 65 import ( 66 "encoding/binary" 67 "image" 68 "math" 69 "time" 70 71 "gioui.org/f32" 72 "gioui.org/internal/ops" 73 ) 74 75 // Ops holds a list of operations. Operations are stored in 76 // serialized form to avoid garbage during construction of 77 // the ops list. 78 type Ops struct { 79 // Internal is for internal use, despite being exported. 80 Internal ops.Ops 81 } 82 83 // MacroOp records a list of operations for later use. 84 type MacroOp struct { 85 ops *ops.Ops 86 id ops.StackID 87 pc ops.PC 88 } 89 90 // CallOp invokes the operations recorded by Record. 91 type CallOp struct { 92 // Ops is the list of operations to invoke. 93 ops *ops.Ops 94 start ops.PC 95 end ops.PC 96 } 97 98 // InvalidateCmd requests a redraw at the given time. Use 99 // the zero value to request an immediate redraw. 100 type InvalidateCmd struct { 101 At time.Time 102 } 103 104 // TransformOp represents a transformation that can be pushed on the 105 // transformation stack. 106 type TransformOp struct { 107 t f32.Affine2D 108 } 109 110 // TransformStack represents a TransformOp pushed on the transformation stack. 111 type TransformStack struct { 112 id ops.StackID 113 macroID uint32 114 ops *ops.Ops 115 } 116 117 // Defer executes c after all other operations have completed, including 118 // previously deferred operations. 119 // Defer saves the transformation stack and pushes it prior to executing 120 // c. All other operation state is reset. 121 // 122 // Note that deferred operations are executed in first-in-first-out order, 123 // unlike the Go facility of the same name. 124 func Defer(o *Ops, c CallOp) { 125 if c.ops == nil { 126 return 127 } 128 state := ops.Save(&o.Internal) 129 // Wrap c in a macro that loads the saved state before execution. 130 m := Record(o) 131 state.Load() 132 c.Add(o) 133 c = m.Stop() 134 // A Defer is recorded as a TypeDefer followed by the 135 // wrapped macro. 136 data := ops.Write(&o.Internal, ops.TypeDeferLen) 137 data[0] = byte(ops.TypeDefer) 138 c.Add(o) 139 } 140 141 // Reset the Ops, preparing it for re-use. Reset invalidates 142 // any recorded macros. 143 func (o *Ops) Reset() { 144 ops.Reset(&o.Internal) 145 } 146 147 // Record a macro of operations. 148 func Record(o *Ops) MacroOp { 149 m := MacroOp{ 150 ops: &o.Internal, 151 id: ops.PushMacro(&o.Internal), 152 pc: ops.PCFor(&o.Internal), 153 } 154 // Reserve room for a macro definition. Updated in Stop. 155 data := ops.Write(m.ops, ops.TypeMacroLen) 156 data[0] = byte(ops.TypeMacro) 157 return m 158 } 159 160 // Stop ends a previously started recording and returns an 161 // operation for replaying it. 162 func (m MacroOp) Stop() CallOp { 163 ops.PopMacro(m.ops, m.id) 164 ops.FillMacro(m.ops, m.pc) 165 return CallOp{ 166 ops: m.ops, 167 // Skip macro header. 168 start: m.pc.Add(ops.TypeMacro), 169 end: ops.PCFor(m.ops), 170 } 171 } 172 173 // Add the recorded list of operations. Add 174 // panics if the Ops containing the recording 175 // has been reset. 176 func (c CallOp) Add(o *Ops) { 177 if c.ops == nil { 178 return 179 } 180 ops.AddCall(&o.Internal, c.ops, c.start, c.end) 181 } 182 183 // Offset converts an offset to a TransformOp. 184 func Offset(off image.Point) TransformOp { 185 offf := f32.Pt(float32(off.X), float32(off.Y)) 186 return Affine(f32.Affine2D{}.Offset(offf)) 187 } 188 189 // Affine creates a TransformOp representing the transformation a. 190 func Affine(a f32.Affine2D) TransformOp { 191 return TransformOp{t: a} 192 } 193 194 // Push the current transformation to the stack and then multiply the 195 // current transformation with t. 196 func (t TransformOp) Push(o *Ops) TransformStack { 197 id, macroID := ops.PushOp(&o.Internal, ops.TransStack) 198 t.add(o, true) 199 return TransformStack{ops: &o.Internal, id: id, macroID: macroID} 200 } 201 202 // Add is like Push except it doesn't push the current transformation to the 203 // stack. 204 func (t TransformOp) Add(o *Ops) { 205 t.add(o, false) 206 } 207 208 func (t TransformOp) add(o *Ops, push bool) { 209 data := ops.Write(&o.Internal, ops.TypeTransformLen) 210 data[0] = byte(ops.TypeTransform) 211 if push { 212 data[1] = 1 213 } 214 bo := binary.LittleEndian 215 a, b, c, d, e, f := t.t.Elems() 216 bo.PutUint32(data[2:], math.Float32bits(a)) 217 bo.PutUint32(data[2+4*1:], math.Float32bits(b)) 218 bo.PutUint32(data[2+4*2:], math.Float32bits(c)) 219 bo.PutUint32(data[2+4*3:], math.Float32bits(d)) 220 bo.PutUint32(data[2+4*4:], math.Float32bits(e)) 221 bo.PutUint32(data[2+4*5:], math.Float32bits(f)) 222 } 223 224 func (t TransformStack) Pop() { 225 ops.PopOp(t.ops, ops.TransStack, t.id, t.macroID) 226 data := ops.Write(t.ops, ops.TypePopTransformLen) 227 data[0] = byte(ops.TypePopTransform) 228 } 229 230 func (InvalidateCmd) ImplementsCommand() {}