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 }