gioui.org/ui@v0.0.0-20190926171558-ce74bc0cbaea/ops.go (about) 1 // SPDX-License-Identifier: Unlicense OR MIT 2 3 package ui 4 5 import ( 6 "encoding/binary" 7 8 "gioui.org/ui/internal/opconst" 9 ) 10 11 // Ops holds a list of operations. Operations are stored in 12 // serialized form to avoid garbage during construction of 13 // the ops list. 14 type Ops struct { 15 // version is incremented at each Reset. 16 version int 17 // data contains the serialized operations. 18 data []byte 19 // External references for operations. 20 refs []interface{} 21 22 stackDepth int 23 macroDepth int 24 25 inAux bool 26 auxOff int 27 auxLen int 28 } 29 30 // StackOp can save and restore the operation state 31 // in a stack-like manner. 32 type StackOp struct { 33 stackDepth int 34 macroDepth int 35 active bool 36 ops *Ops 37 } 38 39 // MacroOp can record a list of operations for later 40 // use. 41 type MacroOp struct { 42 recording bool 43 ops *Ops 44 version int 45 pc pc 46 } 47 48 type pc struct { 49 data int 50 refs int 51 } 52 53 // Push (save) the current operations state. 54 func (s *StackOp) Push(o *Ops) { 55 if s.active { 56 panic("unbalanced push") 57 } 58 s.active = true 59 s.ops = o 60 o.stackDepth++ 61 s.stackDepth = o.stackDepth 62 s.macroDepth = o.macroDepth 63 o.Write([]byte{byte(opconst.TypePush)}) 64 } 65 66 // Pop (restore) a previously Pushed operations state. 67 func (s *StackOp) Pop() { 68 if !s.active { 69 panic("unbalanced pop") 70 } 71 if s.ops.stackDepth != s.stackDepth { 72 panic("unbalanced pop") 73 } 74 if s.ops.macroDepth != s.macroDepth { 75 panic("pop in a different macro than push") 76 } 77 s.active = false 78 s.ops.stackDepth-- 79 s.ops.Write([]byte{byte(opconst.TypePop)}) 80 } 81 82 // Reset the Ops, preparing it for re-use. 83 func (o *Ops) Reset() { 84 o.inAux = false 85 o.stackDepth = 0 86 // Leave references to the GC. 87 for i := range o.refs { 88 o.refs[i] = nil 89 } 90 o.data = o.data[:0] 91 o.refs = o.refs[:0] 92 o.version++ 93 } 94 95 // Internal use only. 96 func (o *Ops) Data() []byte { 97 return o.data 98 } 99 100 // Internal use only. 101 func (o *Ops) Refs() []interface{} { 102 return o.refs 103 } 104 105 // Internal use only. 106 func (o *Ops) Version() int { 107 return o.version 108 } 109 110 // Internal use only. 111 func (o *Ops) Aux() []byte { 112 if !o.inAux { 113 return nil 114 } 115 aux := o.data[o.auxOff+opconst.TypeAuxLen:] 116 return aux[:o.auxLen] 117 } 118 119 func (d *Ops) write(op []byte, refs ...interface{}) { 120 d.data = append(d.data, op...) 121 d.refs = append(d.refs, refs...) 122 } 123 124 // Internal use only. 125 func (o *Ops) Write(op []byte, refs ...interface{}) { 126 t := opconst.OpType(op[0]) 127 if len(refs) != t.NumRefs() { 128 panic("invalid ref count") 129 } 130 switch t { 131 case opconst.TypeAux: 132 // Write only the data. 133 op = op[1:] 134 if !o.inAux { 135 o.inAux = true 136 o.auxOff = o.pc().data 137 o.auxLen = 0 138 header := make([]byte, opconst.TypeAuxLen) 139 header[0] = byte(opconst.TypeAux) 140 o.write(header) 141 } 142 o.auxLen += len(op) 143 default: 144 if o.inAux { 145 o.inAux = false 146 bo := binary.LittleEndian 147 bo.PutUint32(o.data[o.auxOff+1:], uint32(o.auxLen)) 148 } 149 } 150 o.write(op, refs...) 151 } 152 153 func (d *Ops) pc() pc { 154 return pc{data: len(d.data), refs: len(d.refs)} 155 } 156 157 // Record a macro of operations. 158 func (m *MacroOp) Record(o *Ops) { 159 if m.recording { 160 panic("already recording") 161 } 162 m.recording = true 163 m.ops = o 164 m.ops.macroDepth++ 165 m.pc = o.pc() 166 // Reserve room for a macro definition. Updated in Stop. 167 m.ops.Write(make([]byte, opconst.TypeMacroDefLen)) 168 m.fill() 169 } 170 171 // Stop ends a previously started recording. 172 func (m *MacroOp) Stop() { 173 if !m.recording { 174 panic("not recording") 175 } 176 m.ops.macroDepth-- 177 m.recording = false 178 m.fill() 179 } 180 181 func (m *MacroOp) fill() { 182 pc := m.ops.pc() 183 // Fill out the macro definition reserved in Record. 184 data := m.ops.data[m.pc.data:] 185 data = data[:opconst.TypeMacroDefLen] 186 data[0] = byte(opconst.TypeMacroDef) 187 bo := binary.LittleEndian 188 bo.PutUint32(data[1:], uint32(pc.data)) 189 bo.PutUint32(data[5:], uint32(pc.refs)) 190 m.version = m.ops.version 191 } 192 193 // Add the recorded list of operations. The Ops 194 // argument may be different than the Ops argument 195 // passed to Record. 196 func (m MacroOp) Add(o *Ops) { 197 if m.recording { 198 panic("a recording is in progress") 199 } 200 if m.ops == nil { 201 return 202 } 203 data := make([]byte, opconst.TypeMacroLen) 204 data[0] = byte(opconst.TypeMacro) 205 bo := binary.LittleEndian 206 bo.PutUint32(data[1:], uint32(m.pc.data)) 207 bo.PutUint32(data[5:], uint32(m.pc.refs)) 208 bo.PutUint32(data[9:], uint32(m.version)) 209 o.Write(data, m.ops) 210 }