ui: move macro recording from Ops to MacroOp

Move the Record and Stop methods from Ops to MacroOp itself.

Before this change, Ops.Stop stopped the recording of the most
recent macro, which could be a different macro than intended.
After this change, there is no such confusion.

As a bonus, the Ops API becomes less cluttered.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2019-07-16 13:00:56 +02:00
parent ba9ffe846e
commit 94a913a371
6 changed files with 67 additions and 62 deletions
+4 -3
View File
@@ -14,6 +14,7 @@ type Flex struct {
MainAxisAlignment MainAxisAlignment
CrossAxisAlignment CrossAxisAlignment
macro ui.MacroOp
ops *ui.Ops
cs Constraints
mode flexMode
@@ -74,7 +75,7 @@ func (f *Flex) begin(mode flexMode) {
panic("must End before adding a child")
}
f.mode = mode
f.ops.Record()
f.macro.Record(f.ops)
}
func (f *Flex) Rigid() Constraints {
@@ -107,7 +108,7 @@ func (f *Flex) End(dims Dimens) FlexChild {
if f.mode <= modeBegun {
panic("End called without an active child")
}
macro := f.ops.Stop()
f.macro.Stop()
sz := axisMain(f.Axis, dims.Size)
f.size += sz
if f.mode == modeRigid {
@@ -120,7 +121,7 @@ func (f *Flex) End(dims Dimens) FlexChild {
if b := dims.Baseline; b > f.maxBaseline {
f.maxBaseline = b
}
return FlexChild{macro, dims}
return FlexChild{f.macro, dims}
}
func (f *Flex) Layout(children ...FlexChild) Dimens {
+4 -3
View File
@@ -119,6 +119,7 @@ func UniformInset(v ui.Value) Inset {
type Align struct {
Alignment Direction
macro ui.MacroOp
ops *ui.Ops
begun bool
cs Constraints
@@ -131,7 +132,7 @@ func (a *Align) Begin(ops *ui.Ops, cs Constraints) Constraints {
a.begun = true
a.ops = ops
a.cs = cs
ops.Record()
a.macro.Record(ops)
cs.Width.Min = 0
cs.Height.Min = 0
return cs
@@ -143,7 +144,7 @@ func (a *Align) End(dims Dimens) Dimens {
}
a.begun = false
ops := a.ops
macro := ops.Stop()
a.macro.Stop()
sz := dims.Size
if sz.X < a.cs.Width.Min {
sz.X = a.cs.Width.Min
@@ -166,7 +167,7 @@ func (a *Align) End(dims Dimens) Dimens {
}
ui.PushOp{}.Add(ops)
ui.TransformOp{Transform: ui.Offset(toPointF(p))}.Add(ops)
macro.Add(ops)
a.macro.Add(ops)
ui.PopOp{}.Add(ops)
return Dimens{
Size: sz,
+8 -6
View File
@@ -27,6 +27,8 @@ type List struct {
// The distance scrolled since last call to Init.
Distance int
macro ui.MacroOp
child ui.MacroOp
ops *ui.Ops
scroll gesture.Scroll
scrollDir int
@@ -70,7 +72,7 @@ func (l *List) Init(ops *ui.Ops, cs Constraints, len int) {
if l.first > len {
l.first = len
}
ops.Record()
l.macro.Record(ops)
l.Next()
}
@@ -103,7 +105,7 @@ func (l *List) Next() {
i = l.len - 1 - i
}
l.index = i
l.ops.Record()
l.child.Record(l.ops)
}
// Index is the current element index.
@@ -146,8 +148,8 @@ func (l *List) next() (int, bool) {
// Elem completes an element.
func (l *List) Elem(dims Dimens) {
macro := l.ops.Stop()
child := scrollChild{dims.Size, macro}
l.child.Stop()
child := scrollChild{dims.Size, l.child}
switch l.dir {
case iterateForward:
mainSize := axisMain(l.Axis, child.size)
@@ -237,9 +239,9 @@ func (l *List) Layout() Dimens {
l.scroll.Stop()
}
dims := axisPoint(l.Axis, mainc.Constrain(pos), maxCross)
macro := ops.Stop()
l.macro.Stop()
pointer.RectAreaOp{Size: dims}.Add(ops)
l.scroll.Add(ops)
macro.Add(ops)
l.macro.Add(ops)
return Dimens{Size: dims}
}
+4 -3
View File
@@ -11,6 +11,7 @@ import (
type Stack struct {
Alignment Direction
macro ui.MacroOp
ops *ui.Ops
constrained bool
cs Constraints
@@ -54,7 +55,7 @@ func (s *Stack) begin() {
panic("must End before adding a child")
}
s.begun = true
s.ops.Record()
s.macro.Record(s.ops)
}
func (s *Stack) Rigid() Constraints {
@@ -71,7 +72,7 @@ func (s *Stack) Expand() Constraints {
}
func (s *Stack) End(dims Dimens) StackChild {
b := s.ops.Stop()
s.macro.Stop()
s.begun = false
if w := dims.Size.X; w > s.maxSZ.X {
s.maxSZ.X = w
@@ -84,7 +85,7 @@ func (s *Stack) End(dims Dimens) StackChild {
s.baseline = b
}
}
return StackChild{b, dims}
return StackChild{s.macro, dims}
}
func (s *Stack) Layout(children ...StackChild) Dimens {
+4 -2
View File
@@ -234,7 +234,8 @@ func textPath(ppem fixed.Int26_6, f *opentype, str text.String) ui.MacroOp {
builder.Init(ops)
var x fixed.Int26_6
var advIdx int
ops.Record()
var m ui.MacroOp
m.Record(ops)
for _, r := range str.String {
if !unicode.IsSpace(r) {
segs, ok := f.LoadGlyph(ppem, r)
@@ -287,5 +288,6 @@ func textPath(ppem fixed.Int26_6, f *opentype, str text.String) ui.MacroOp {
advIdx++
}
builder.End()
return ops.Stop()
m.Stop()
return m
}
+43 -45
View File
@@ -2,16 +2,12 @@ package ui
import (
"encoding/binary"
"errors"
"fmt"
"gioui.org/ui/internal/ops"
)
// Ops holds a list of serialized Ops.
type Ops struct {
// Stack of macro start indices.
stack []pc
version int
// Serialized ops.
data []byte
@@ -61,9 +57,10 @@ type PushOp struct{}
type PopOp struct{}
type MacroOp struct {
ops *Ops
version int
pc pc
recording bool
ops *Ops
version int
pc pc
}
type opMacroDef struct {
@@ -82,14 +79,6 @@ func (p PopOp) Add(o *Ops) {
o.Write([]byte{byte(ops.TypePop)})
}
// Record starts recording a macro. Multiple simultaneous
// recordings are supported. Stop ends the most recent.
func (o *Ops) Record() {
o.stack = append(o.stack, o.pc())
// Make room for a macro definition. Filled out in Stop.
o.Write(make([]byte, ops.TypeMacroDefLen))
}
func (op *opAux) decode(data []byte) {
if ops.OpType(data[0]) != ops.TypeAux {
panic("invalid op")
@@ -115,28 +104,9 @@ func (op *opMacroDef) decode(data []byte) {
}
}
// Stop the most recent recording and return the macro for later
// use.
func (o *Ops) Stop() MacroOp {
if len(o.stack) == 0 {
panic(errors.New("not recording a macro"))
}
start := o.stack[len(o.stack)-1]
o.stack = o.stack[:len(o.stack)-1]
pc := o.pc()
// Write the macro header reserved in Begin.
data := o.data[start.data : start.data+ops.TypeMacroDefLen]
data[0] = byte(ops.TypeMacroDef)
bo := binary.LittleEndian
bo.PutUint32(data[1:], uint32(pc.data))
bo.PutUint32(data[5:], uint32(pc.refs))
return MacroOp{ops: o, pc: start, version: o.version}
}
// Reset the Ops, preparing it for re-use.
func (o *Ops) Reset() {
o.inAux = false
o.stack = o.stack[:0]
// Leave references to the GC.
for i := range o.refs {
o.refs[i] = nil
@@ -191,7 +161,35 @@ func (d *Ops) pc() pc {
return pc{data: len(d.data), refs: len(d.refs)}
}
func (b *MacroOp) decode(data []byte, refs []interface{}) {
// Record a macro of operations.
func (m *MacroOp) Record(o *Ops) {
if m.recording {
panic("already recording")
}
m.recording = true
m.ops = o
m.pc = o.pc()
// Make room for a macro definition. Filled out in Stop.
m.ops.Write(make([]byte, ops.TypeMacroDefLen))
}
// Stop recording the macro.
func (m *MacroOp) Stop() {
if !m.recording {
panic("not recording")
}
m.recording = false
pc := m.ops.pc()
// Fill out the macro definition reserved in Record.
data := m.ops.data[m.pc.data : m.pc.data+ops.TypeMacroDefLen]
data[0] = byte(ops.TypeMacroDef)
bo := binary.LittleEndian
bo.PutUint32(data[1:], uint32(pc.data))
bo.PutUint32(data[5:], uint32(pc.refs))
m.version = m.ops.version
}
func (m *MacroOp) decode(data []byte, refs []interface{}) {
if ops.OpType(data[0]) != ops.TypeMacro {
panic("invalid op")
}
@@ -199,7 +197,7 @@ func (b *MacroOp) decode(data []byte, refs []interface{}) {
dataIdx := int(bo.Uint32(data[1:]))
refsIdx := int(bo.Uint32(data[5:]))
version := int(bo.Uint32(data[9:]))
*b = MacroOp{
*m = MacroOp{
ops: refs[0].(*Ops),
pc: pc{
data: dataIdx,
@@ -209,17 +207,20 @@ func (b *MacroOp) decode(data []byte, refs []interface{}) {
}
}
func (b MacroOp) Add(o *Ops) {
if b.ops == nil {
func (m MacroOp) Add(o *Ops) {
if m.recording {
panic("a recording is in progress")
}
if m.ops == nil {
return
}
data := make([]byte, ops.TypeMacroLen)
data[0] = byte(ops.TypeMacro)
bo := binary.LittleEndian
bo.PutUint32(data[1:], uint32(b.pc.data))
bo.PutUint32(data[5:], uint32(b.pc.refs))
bo.PutUint32(data[9:], uint32(b.version))
o.Write(data, b.ops)
bo.PutUint32(data[1:], uint32(m.pc.data))
bo.PutUint32(data[5:], uint32(m.pc.refs))
bo.PutUint32(data[9:], uint32(m.version))
o.Write(data, m.ops)
}
// Reset start reading from the op list.
@@ -230,9 +231,6 @@ func (r *OpsReader) Reset(ops *Ops) {
if ops == nil {
return
}
if n := len(ops.stack); n > 0 {
panic(fmt.Errorf("%d Begin(s) not matched with End", n))
}
r.ops = ops
}