From 586d33c26e58b076f5f11bd842ca50a82be21f9c Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Tue, 16 Jul 2019 13:41:10 +0200 Subject: [PATCH] ui: replace PushOp, PopOp with a StackOp Before this change, there was no guarantee that a PopOp matched the intended PushOp. With a single stack operation, the client is forced to match pop with the right push. Signed-off-by: Elias Naur --- ui/layout/flex.go | 5 +++-- ui/layout/layout.go | 13 ++++++------- ui/layout/list.go | 5 +++-- ui/layout/stack.go | 5 +++-- ui/ops.go | 32 ++++++++++++++++++++++++++------ ui/text/editor.go | 10 ++++++---- ui/text/label.go | 5 +++-- 7 files changed, 50 insertions(+), 25 deletions(-) diff --git a/ui/layout/flex.go b/ui/layout/flex.go index e770479e..badbf331 100644 --- a/ui/layout/flex.go +++ b/ui/layout/flex.go @@ -157,12 +157,13 @@ func (f *Flex) Layout(children ...FlexChild) Dimens { cross = f.maxBaseline - b } } - ui.PushOp{}.Add(f.ops) + var stack ui.StackOp + stack.Push(f.ops) ui.TransformOp{ Transform: ui.Offset(toPointF(axisPoint(f.Axis, mainSize, cross))), }.Add(f.ops) child.macro.Add(f.ops) - ui.PopOp{}.Add(f.ops) + stack.Pop() mainSize += axisMain(f.Axis, dims.Size) if i < len(children)-1 { switch f.MainAxisAlignment { diff --git a/ui/layout/layout.go b/ui/layout/layout.go index 33defa94..f433855c 100644 --- a/ui/layout/layout.go +++ b/ui/layout/layout.go @@ -54,8 +54,8 @@ func RigidConstraints(size image.Point) Constraints { type Inset struct { Top, Right, Bottom, Left ui.Value + stack ui.StackOp top, right, bottom, left int - ops *ui.Ops begun bool cs Constraints } @@ -69,7 +69,6 @@ func (in *Inset) Begin(c ui.Config, ops *ui.Ops, cs Constraints) Constraints { in.bottom = c.Px(in.Bottom) in.left = c.Px(in.Left) in.begun = true - in.ops = ops in.cs = cs mcs := cs if mcs.Width.Max != ui.Inf { @@ -92,7 +91,7 @@ func (in *Inset) Begin(c ui.Config, ops *ui.Ops, cs Constraints) Constraints { mcs.Height.Max = mcs.Height.Min } } - ui.PushOp{}.Add(ops) + in.stack.Push(ops) ui.TransformOp{Transform: ui.Offset(toPointF(image.Point{X: in.left, Y: in.top}))}.Add(ops) return mcs } @@ -102,8 +101,7 @@ func (in *Inset) End(dims Dimens) Dimens { panic("must Begin before End") } in.begun = false - ops := in.ops - ui.PopOp{}.Add(ops) + in.stack.Pop() return Dimens{ Size: in.cs.Constrain(dims.Size.Add(image.Point{X: in.right + in.left, Y: in.top + in.bottom})), Baseline: dims.Baseline + in.top, @@ -165,10 +163,11 @@ func (a *Align) End(dims Dimens) Dimens { case SW, S, SE: p.Y = sz.Y - dims.Size.Y } - ui.PushOp{}.Add(ops) + var stack ui.StackOp + stack.Push(ops) ui.TransformOp{Transform: ui.Offset(toPointF(p))}.Add(ops) a.macro.Add(ops) - ui.PopOp{}.Add(ops) + stack.Pop() return Dimens{ Size: sz, Baseline: dims.Baseline, diff --git a/ui/layout/list.go b/ui/layout/list.go index ef4062dc..1157978e 100644 --- a/ui/layout/list.go +++ b/ui/layout/list.go @@ -224,13 +224,14 @@ func (l *List) Layout() Dimens { Min: axisPoint(l.Axis, min, -ui.Inf), Max: axisPoint(l.Axis, max, ui.Inf), } - ui.PushOp{}.Add(ops) + var stack ui.StackOp + stack.Push(ops) draw.RectClip(r).Add(ops) ui.TransformOp{ Transform: ui.Offset(toPointF(axisPoint(l.Axis, transPos, cross))), }.Add(ops) child.macro.Add(ops) - ui.PopOp{}.Add(ops) + stack.Pop() pos += childSize } atStart := l.first == 0 && l.offset <= 0 diff --git a/ui/layout/stack.go b/ui/layout/stack.go index 85a0616c..7edc4093 100644 --- a/ui/layout/stack.go +++ b/ui/layout/stack.go @@ -104,10 +104,11 @@ func (s *Stack) Layout(children ...StackChild) Dimens { case SW, S, SE: p.Y = s.maxSZ.Y - sz.Y } - ui.PushOp{}.Add(s.ops) + var stack ui.StackOp + stack.Push(s.ops) ui.TransformOp{Transform: ui.Offset(toPointF(p))}.Add(s.ops) ch.macro.Add(s.ops) - ui.PopOp{}.Add(s.ops) + stack.Pop() } b := s.baseline if b == 0 { diff --git a/ui/ops.go b/ui/ops.go index cd420777..598532a5 100644 --- a/ui/ops.go +++ b/ui/ops.go @@ -14,6 +14,8 @@ type Ops struct { // Op references. refs []interface{} + stackDepth int + inAux bool auxOff int auxLen int @@ -52,9 +54,11 @@ type pc struct { refs int } -type PushOp struct{} - -type PopOp struct{} +type StackOp struct { + depth int + active bool + ops *Ops +} type MacroOp struct { recording bool @@ -71,12 +75,27 @@ type opAux struct { len int } -func (p PushOp) Add(o *Ops) { +func (s *StackOp) Push(o *Ops) { + if s.active { + panic("unbalanced push") + } + s.active = true + s.ops = o + o.stackDepth++ + s.depth = o.stackDepth o.Write([]byte{byte(ops.TypePush)}) } -func (p PopOp) Add(o *Ops) { - o.Write([]byte{byte(ops.TypePop)}) +func (s *StackOp) Pop() { + if !s.active { + panic("unbalanced pop") + } + d := s.ops.stackDepth + if d != s.depth { + panic("unbalanced pop") + } + s.ops.stackDepth-- + s.ops.Write([]byte{byte(ops.TypePop)}) } func (op *opAux) decode(data []byte) { @@ -107,6 +126,7 @@ func (op *opMacroDef) decode(data []byte) { // Reset the Ops, preparing it for re-use. func (o *Ops) Reset() { o.inAux = false + o.stackDepth = 0 // Leave references to the GC. for i := range o.refs { o.refs[i] = nil diff --git a/ui/text/editor.go b/ui/text/editor.go index bd4b03e8..008e4072 100644 --- a/ui/text/editor.go +++ b/ui/text/editor.go @@ -194,7 +194,8 @@ func (e *Editor) Layout(ops *ui.Ops, cs layout.Constraints) layout.Dimens { Width: e.viewWidth(), Offset: off, } - ui.PushOp{}.Add(ops) + var stack ui.StackOp + stack.Push(ops) // Apply material. Set a default color in case the material is empty. if e.rr.len() > 0 { draw.ColorOp{Color: color.RGBA{A: 0xff}}.Add(ops) @@ -208,11 +209,12 @@ func (e *Editor) Layout(ops *ui.Ops, cs layout.Constraints) layout.Dimens { if !ok { break } - ui.PushOp{}.Add(ops) + var stack ui.StackOp + stack.Push(ops) ui.TransformOp{Transform: ui.Offset(lineOff)}.Add(ops) e.Face.Path(str).Add(ops) draw.DrawOp{Rect: toRectF(clip).Sub(lineOff)}.Add(ops) - ui.PopOp{}.Add(ops) + stack.Pop() } if e.focused { now := e.Config.Now() @@ -245,7 +247,7 @@ func (e *Editor) Layout(ops *ui.Ops, cs layout.Constraints) layout.Dimens { redraw.Add(ops) } } - ui.PopOp{}.Add(ops) + stack.Pop() baseline := e.padTop + e.dims.Baseline pointer.RectAreaOp{Size: e.viewSize}.Add(ops) diff --git a/ui/text/label.go b/ui/text/label.go index 56d21e23..b3b91178 100644 --- a/ui/text/label.go +++ b/ui/text/label.go @@ -107,14 +107,15 @@ func (l Label) Layout(ops *ui.Ops, cs layout.Constraints) layout.Dimens { break } lclip := toRectF(clip).Sub(off) - ui.PushOp{}.Add(ops) + var stack ui.StackOp + stack.Push(ops) ui.TransformOp{Transform: ui.Offset(off)}.Add(ops) l.Face.Path(str).Add(ops) // Set a default color in case the material is empty. draw.ColorOp{Color: color.RGBA{A: 0xff}}.Add(ops) l.Material.Add(ops) draw.DrawOp{Rect: lclip}.Add(ops) - ui.PopOp{}.Add(ops) + stack.Pop() } return dims }