From 94a913a371c43b5df8ac688f7e8b58661fafb1cd Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Tue, 16 Jul 2019 13:00:56 +0200 Subject: [PATCH] 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 --- ui/layout/flex.go | 7 ++-- ui/layout/layout.go | 7 ++-- ui/layout/list.go | 14 ++++--- ui/layout/stack.go | 7 ++-- ui/measure/measure.go | 6 ++- ui/ops.go | 88 +++++++++++++++++++++---------------------- 6 files changed, 67 insertions(+), 62 deletions(-) diff --git a/ui/layout/flex.go b/ui/layout/flex.go index 03130f86..e770479e 100644 --- a/ui/layout/flex.go +++ b/ui/layout/flex.go @@ -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 { diff --git a/ui/layout/layout.go b/ui/layout/layout.go index cf5755cc..33defa94 100644 --- a/ui/layout/layout.go +++ b/ui/layout/layout.go @@ -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, diff --git a/ui/layout/list.go b/ui/layout/list.go index 6ab37ff3..ef4062dc 100644 --- a/ui/layout/list.go +++ b/ui/layout/list.go @@ -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} } diff --git a/ui/layout/stack.go b/ui/layout/stack.go index 72fe54a2..85a0616c 100644 --- a/ui/layout/stack.go +++ b/ui/layout/stack.go @@ -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 { diff --git a/ui/measure/measure.go b/ui/measure/measure.go index e67dbcf7..6f93fcee 100644 --- a/ui/measure/measure.go +++ b/ui/measure/measure.go @@ -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 } diff --git a/ui/ops.go b/ui/ops.go index e439ffda..cd420777 100644 --- a/ui/ops.go +++ b/ui/ops.go @@ -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 }