From ae8a377cda62204d4c3fdfe0e5b481e1cbeadc41 Mon Sep 17 00:00:00 2001 From: Thomas Bruyelle Date: Thu, 28 May 2020 10:50:48 +0200 Subject: [PATCH] op: add op.Push and op.Record funcs The funcs replace stack.Push and macro.Record, which become private. This makes stack and macro faster to write, in particular for stacks where you can just write the following line to save and restore the state : defer op.Push(ops).Pop() This usage requires Push to return a pointer (since Pop has a pointer receiver), or else the code doesn't compile. For consistancy, I tried to do the same for op.Record, but this implied to turn all the MacroOp fields into pointers, and this caused some panics. As a result, op.Record doesn't return a pointer. An other side effect pointed by Larry Clapp: StackOp and MacroOp are not re-usable any more, you have to allocate a new one for each usage, using the described funcs above. Signed-off-by: Thomas Bruyelle --- io/router/pointer_test.go | 5 +---- layout/flex.go | 17 +++++++---------- layout/layout.go | 9 +++------ layout/list.go | 11 ++++------- layout/stack.go | 17 +++++++---------- op/clip/clip.go | 2 +- op/op.go | 27 +++++++++++++++++++++------ widget/bool.go | 4 +--- widget/button.go | 5 ++--- widget/editor.go | 6 ++---- widget/enum.go | 4 +--- widget/image.go | 5 ++--- widget/label.go | 3 +-- widget/material/button.go | 3 +-- widget/material/editor.go | 7 ++----- widget/material/switch.go | 16 ++++++---------- 16 files changed, 62 insertions(+), 79 deletions(-) diff --git a/io/router/pointer_test.go b/io/router/pointer_test.go index 6ea80d7a..94fda006 100644 --- a/io/router/pointer_test.go +++ b/io/router/pointer_test.go @@ -311,7 +311,6 @@ func TestPointerEnterLeaveNested(t *testing.T) { func TestPointerActiveInputDisappears(t *testing.T) { handler1 := new(int) - // Save this logic so we can redo it later. var ops op.Ops var r Router @@ -386,9 +385,7 @@ func TestMultitouch(t *testing.T) { // addPointerHandler adds a pointer.InputOp for the tag in a // rectangular area. func addPointerHandler(ops *op.Ops, tag event.Tag, area image.Rectangle) { - var stack op.StackOp - stack.Push(ops) - defer stack.Pop() + defer op.Push(ops).Pop() pointer.Rect(area).Add(ops) pointer.InputOp{Tag: tag}.Add(ops) } diff --git a/layout/flex.go b/layout/flex.go index a31077e0..7037d923 100644 --- a/layout/flex.go +++ b/layout/flex.go @@ -89,15 +89,14 @@ func (f Flex) Layout(gtx Context, children ...FlexChild) Dimensions { } crossMin, crossMax := axisCrossConstraint(f.Axis, cs) cs = axisConstraints(f.Axis, 0, mainMax, crossMin, crossMax) - var m op.MacroOp - m.Record(gtx.Ops) + macro := op.Record(gtx.Ops) gtx := gtx gtx.Constraints = cs dims := child.widget(gtx) - m.Stop() + macro.Stop() sz := axisMain(f.Axis, dims.Size) size += sz - children[i].macro = m + children[i].macro = macro children[i].dims = dims } rigidSize := size @@ -124,15 +123,14 @@ func (f Flex) Layout(gtx Context, children ...FlexChild) Dimensions { } crossMin, crossMax := axisCrossConstraint(f.Axis, cs) cs = axisConstraints(f.Axis, flexSize, flexSize, crossMin, crossMax) - var m op.MacroOp - m.Record(gtx.Ops) + macro := op.Record(gtx.Ops) gtx := gtx gtx.Constraints = cs dims := child.widget(gtx) - m.Stop() + macro.Stop() sz := axisMain(f.Axis, dims.Size) size += sz - children[i].macro = m + children[i].macro = macro children[i].dims = dims } var maxCross int @@ -176,8 +174,7 @@ func (f Flex) Layout(gtx Context, children ...FlexChild) Dimensions { cross = maxBaseline - b } } - var stack op.StackOp - stack.Push(gtx.Ops) + stack := op.Push(gtx.Ops) op.TransformOp{}.Offset(FPt(axisPoint(f.Axis, mainSize, cross))).Add(gtx.Ops) child.macro.Add() stack.Pop() diff --git a/layout/layout.go b/layout/layout.go index a92df55b..4930c168 100644 --- a/layout/layout.go +++ b/layout/layout.go @@ -135,8 +135,7 @@ func (in Inset) Layout(gtx Context, w Widget) Dimensions { if mcs.Min.Y > mcs.Max.Y { mcs.Min.Y = mcs.Max.Y } - var stack op.StackOp - stack.Push(gtx.Ops) + stack := op.Push(gtx.Ops) op.TransformOp{}.Offset(FPt(image.Point{X: left, Y: top})).Add(gtx.Ops) gtx.Constraints = mcs dims := w(gtx) @@ -155,8 +154,7 @@ func UniformInset(v unit.Value) Inset { // Layout a widget according to the direction. func (a Direction) Layout(gtx Context, w Widget) Dimensions { - var macro op.MacroOp - macro.Record(gtx.Ops) + macro := op.Record(gtx.Ops) cs := gtx.Constraints gtx.Constraints.Min = image.Point{} dims := w(gtx) @@ -181,8 +179,7 @@ func (a Direction) Layout(gtx Context, w Widget) Dimensions { case SW, S, SE: p.Y = sz.Y - dims.Size.Y } - var stack op.StackOp - stack.Push(gtx.Ops) + stack := op.Push(gtx.Ops) op.TransformOp{}.Offset(FPt(p)).Add(gtx.Ops) macro.Add() stack.Pop() diff --git a/layout/list.go b/layout/list.go index ecc908f8..64ae1c9d 100644 --- a/layout/list.go +++ b/layout/list.go @@ -95,7 +95,7 @@ func (l *List) init(gtx Context, len int) { l.Position.Offset = 0 l.Position.First = len } - l.macro.Record(gtx.Ops) + l.macro = op.Record(gtx.Ops) l.next() } @@ -137,7 +137,7 @@ func (l *List) next() { l.dir = l.nextDir() } if l.more() { - l.child.Record(l.ctx.Ops) + l.child = op.Record(l.ctx.Ops) } } @@ -257,8 +257,7 @@ func (l *List) layout() Dimensions { Min: axisPoint(l.Axis, min, -inf), Max: axisPoint(l.Axis, max, inf), } - var stack op.StackOp - stack.Push(ops) + stack := op.Push(ops) clip.Rect{Rect: FRect(r)}.Op(ops).Add(ops) op.TransformOp{}.Offset(FPt(axisPoint(l.Axis, pos, cross))).Add(ops) child.macro.Add() @@ -279,9 +278,7 @@ func (l *List) layout() Dimensions { } dims := axisPoint(l.Axis, pos, maxCross) l.macro.Stop() - var st op.StackOp - st.Push(l.ctx.Ops) - defer st.Pop() + defer op.Push(l.ctx.Ops).Pop() pointer.Rect(image.Rectangle{Max: dims}).Add(ops) l.scroll.Add(ops) l.macro.Add() diff --git a/layout/stack.go b/layout/stack.go index 447547b2..fd030e8d 100644 --- a/layout/stack.go +++ b/layout/stack.go @@ -54,19 +54,18 @@ func (s Stack) Layout(gtx Context, children ...StackChild) Dimensions { if w.expanded { continue } - var m op.MacroOp - m.Record(gtx.Ops) + macro := op.Record(gtx.Ops) gtx := gtx gtx.Constraints.Min = image.Pt(0, 0) dims := w.widget(gtx) - m.Stop() + macro.Stop() if w := dims.Size.X; w > maxSZ.X { maxSZ.X = w } if h := dims.Size.Y; h > maxSZ.Y { maxSZ.Y = h } - children[i].macro = m + children[i].macro = macro children[i].dims = dims } // Then lay out Expanded children. @@ -74,21 +73,20 @@ func (s Stack) Layout(gtx Context, children ...StackChild) Dimensions { if !w.expanded { continue } - var m op.MacroOp - m.Record(gtx.Ops) + macro := op.Record(gtx.Ops) gtx := gtx gtx.Constraints = Constraints{ Min: maxSZ, Max: gtx.Constraints.Max, } dims := w.widget(gtx) - m.Stop() + macro.Stop() if w := dims.Size.X; w > maxSZ.X { maxSZ.X = w } if h := dims.Size.Y; h > maxSZ.Y { maxSZ.Y = h } - children[i].macro = m + children[i].macro = macro children[i].dims = dims } @@ -109,8 +107,7 @@ func (s Stack) Layout(gtx Context, children ...StackChild) Dimensions { case SW, S, SE: p.Y = maxSZ.Y - sz.Y } - var stack op.StackOp - stack.Push(gtx.Ops) + stack := op.Push(gtx.Ops) op.TransformOp{}.Offset(FPt(p)).Add(gtx.Ops) ch.macro.Add() stack.Pop() diff --git a/op/clip/clip.go b/op/clip/clip.go index 6a1f47c2..c54bdcc8 100644 --- a/op/clip/clip.go +++ b/op/clip/clip.go @@ -53,7 +53,7 @@ func (p Op) Add(o *op.Ops) { // Begin the path, storing the path data and final Op into ops. func (p *Path) Begin(ops *op.Ops) { p.ops = ops - p.macro.Record(ops) + p.macro = op.Record(ops) // Write the TypeAux opcode and a byte for marking whether the // path has had its MaxY filled out. If not, the gpu will fill it // before using it. diff --git a/op/op.go b/op/op.go index 49a3dc02..6a230e3b 100644 --- a/op/op.go +++ b/op/op.go @@ -36,15 +36,19 @@ mutable state stack and execution flow can be controlled with macros. The StackOp saves the current state to the state stack and restores it later: ops := new(op.Ops) - var stack op.StackOp // Save the current state, in particular the transform. - stack.Push(ops) + stack := op.Push(ops) // Apply a transform to subsequent operations. op.TransformOp{}.Offset(...).Add(ops) ... // Restore the previous transform. stack.Pop() +You can also use this one-line to save the current state and restore it at the +end of a function : + + defer op.Push(ops).Pop() + The CallOp invokes another operation list: ops := new(op.Ops) @@ -54,8 +58,7 @@ The CallOp invokes another operation list: The MacroOp records a list of operations to be executed later: ops := new(op.Ops) - var macro op.MacroOp - macro.Record(ops) + macro := op.Record(ops) // Record operations by adding them. op.InvalidateOp{}.Add(ops) ... @@ -155,7 +158,13 @@ func (c CallOp) Add(o *Ops) { } // Push (save) the current operations state. -func (s *StackOp) Push(o *Ops) { +func Push(o *Ops) *StackOp { + var s StackOp + s.push(o) + return &s +} + +func (s *StackOp) push(o *Ops) { if s.active { panic("unbalanced push") } @@ -221,7 +230,13 @@ func (o *Ops) pc() pc { } // Record a macro of operations. -func (m *MacroOp) Record(o *Ops) { +func Record(o *Ops) MacroOp { + var m MacroOp + m.record(o) + return m +} + +func (m *MacroOp) record(o *Ops) { if m.recording { panic("already recording") } diff --git a/widget/bool.go b/widget/bool.go index 270e51fc..f2f0cc6d 100644 --- a/widget/bool.go +++ b/widget/bool.go @@ -39,9 +39,7 @@ func (b *Bool) Layout(gtx layout.Context) layout.Dimensions { b.changed = true } } - var st op.StackOp - st.Push(gtx.Ops) - defer st.Pop() + defer op.Push(gtx.Ops).Pop() pointer.Rect(image.Rectangle{Max: gtx.Constraints.Min}).Add(gtx.Ops) b.gesture.Add(gtx.Ops) return layout.Dimensions{Size: gtx.Constraints.Min} diff --git a/widget/button.go b/widget/button.go index 2b890603..6a1ddbed 100644 --- a/widget/button.go +++ b/widget/button.go @@ -67,11 +67,10 @@ func (b *Clickable) History() []Press { func (b *Clickable) Layout(gtx layout.Context) layout.Dimensions { b.update(gtx) - var st op.StackOp - st.Push(gtx.Ops) + stack := op.Push(gtx.Ops) pointer.Rect(image.Rectangle{Max: gtx.Constraints.Min}).Add(gtx.Ops) b.click.Add(gtx.Ops) - st.Pop() + stack.Pop() for len(b.history) > 0 { c := b.history[0] if gtx.Now().Sub(c.Time) < 1*time.Second { diff --git a/widget/editor.go b/widget/editor.go index b3fe246a..27c5d640 100644 --- a/widget/editor.go +++ b/widget/editor.go @@ -328,8 +328,7 @@ func (e *Editor) PaintText(gtx layout.Context) { clip := textPadding(e.lines) clip.Max = clip.Max.Add(e.viewSize) for _, shape := range e.shapes { - var stack op.StackOp - stack.Push(gtx.Ops) + stack := op.Push(gtx.Ops) op.TransformOp{}.Offset(shape.offset).Add(gtx.Ops) shape.clip.Add(gtx.Ops) paint.PaintOp{Rect: layout.FRect(clip).Sub(shape.offset)}.Add(gtx.Ops) @@ -344,8 +343,7 @@ func (e *Editor) PaintCaret(gtx layout.Context) { carWidth := fixed.I(gtx.Px(unit.Dp(1))) carLine, _, carX, carY := e.layoutCaret() - var stack op.StackOp - stack.Push(gtx.Ops) + stack := op.Push(gtx.Ops) carX -= carWidth / 2 carAsc, carDesc := -e.lines[carLine].Bounds.Min.Y, e.lines[carLine].Bounds.Max.Y carRect := image.Rectangle{ diff --git a/widget/enum.go b/widget/enum.go index 70dc90f5..79b681cc 100644 --- a/widget/enum.go +++ b/widget/enum.go @@ -37,9 +37,7 @@ func (e *Enum) Changed() bool { // Layout adds the event handler for key. func (e *Enum) Layout(gtx layout.Context, key string) layout.Dimensions { - var st op.StackOp - st.Push(gtx.Ops) - defer st.Pop() + defer op.Push(gtx.Ops).Pop() pointer.Rect(image.Rectangle{Max: gtx.Constraints.Min}).Add(gtx.Ops) if index(e.values, key) == -1 { diff --git a/widget/image.go b/widget/image.go index 15a3ca83..5ade15d5 100644 --- a/widget/image.go +++ b/widget/image.go @@ -33,11 +33,10 @@ func (im Image) Layout(gtx layout.Context) layout.Dimensions { w, h := gtx.Px(unit.Dp(wf*scale)), gtx.Px(unit.Dp(hf*scale)) cs := gtx.Constraints d := cs.Constrain(image.Pt(w, h)) - var s op.StackOp - s.Push(gtx.Ops) + stack := op.Push(gtx.Ops) clip.Rect{Rect: f32.Rectangle{Max: layout.FPt(d)}}.Op(gtx.Ops).Add(gtx.Ops) im.Src.Add(gtx.Ops) paint.PaintOp{Rect: f32.Rectangle{Max: f32.Point{X: float32(w), Y: float32(h)}}}.Add(gtx.Ops) - s.Pop() + stack.Pop() return layout.Dimensions{Size: d} } diff --git a/widget/label.go b/widget/label.go index cab5aeec..45c35875 100644 --- a/widget/label.go +++ b/widget/label.go @@ -107,8 +107,7 @@ func (l Label) Layout(gtx layout.Context, s text.Shaper, font text.Font, size un break } lclip := layout.FRect(clip).Sub(off) - var stack op.StackOp - stack.Push(gtx.Ops) + stack := op.Push(gtx.Ops) op.TransformOp{}.Offset(off).Add(gtx.Ops) str := txt[start:end] s.ShapeString(font, textSize, str, l).Add(gtx.Ops) diff --git a/widget/material/button.go b/widget/material/button.go index bb5f7f31..0d28e537 100644 --- a/widget/material/button.go +++ b/widget/material/button.go @@ -188,8 +188,7 @@ func drawInk(gtx layout.Context, c widget.Press) { return } t = t / duration - var stack op.StackOp - stack.Push(gtx.Ops) + stack := op.Push(gtx.Ops) size := float32(gtx.Px(unit.Dp(700))) * t rr := size * .5 col := byte(0xaa * (1 - t*t)) diff --git a/widget/material/editor.go b/widget/material/editor.go index 1a4dd719..96739924 100644 --- a/widget/material/editor.go +++ b/widget/material/editor.go @@ -39,10 +39,8 @@ func Editor(th *Theme, editor *widget.Editor, hint string) EditorStyle { } func (e EditorStyle) Layout(gtx layout.Context) layout.Dimensions { - var stack op.StackOp - stack.Push(gtx.Ops) - var macro op.MacroOp - macro.Record(gtx.Ops) + defer op.Push(gtx.Ops).Pop() + macro := op.Record(gtx.Ops) paint.ColorOp{Color: e.HintColor}.Add(gtx.Ops) tl := widget.Label{Alignment: e.Editor.Alignment} dims := tl.Layout(gtx, e.shaper, e.Font, e.TextSize, e.Hint) @@ -62,6 +60,5 @@ func (e EditorStyle) Layout(gtx layout.Context) layout.Dimensions { } paint.ColorOp{Color: e.Color}.Add(gtx.Ops) e.Editor.PaintCaret(gtx) - stack.Pop() return dims } diff --git a/widget/material/switch.go b/widget/material/switch.go index 401b1dcf..bc71e6a8 100644 --- a/widget/material/switch.go +++ b/widget/material/switch.go @@ -36,8 +36,7 @@ func (s SwitchStyle) Layout(gtx layout.Context) layout.Dimensions { trackOff := float32(thumbSize-trackHeight) * .5 // Draw track. - var stack op.StackOp - stack.Push(gtx.Ops) + stack := op.Push(gtx.Ops) trackCorner := float32(trackHeight) / 2 trackRect := f32.Rectangle{Max: f32.Point{ X: float32(trackWidth), @@ -53,7 +52,7 @@ func (s SwitchStyle) Layout(gtx layout.Context) layout.Dimensions { stack.Pop() // Compute thumb offset and color. - stack.Push(gtx.Ops) + stack = op.Push(gtx.Ops) col := rgb(0xffffff) if s.Switch.Value { off := trackWidth - thumbSize @@ -63,8 +62,7 @@ func (s SwitchStyle) Layout(gtx layout.Context) layout.Dimensions { // Draw thumb shadow, a translucent disc slightly larger than the // thumb itself. - var shadowStack op.StackOp - shadowStack.Push(gtx.Ops) + shadowStack := op.Push(gtx.Ops) shadowSize := float32(2) // Center shadow horizontally and slightly adjust its Y. op.TransformOp{}.Offset(f32.Point{X: -shadowSize / 2, Y: -.75}).Add(gtx.Ops) @@ -76,7 +74,7 @@ func (s SwitchStyle) Layout(gtx layout.Context) layout.Dimensions { stack.Pop() // Draw thumb ink. - stack.Push(gtx.Ops) + stack = op.Push(gtx.Ops) inkSize := float32(gtx.Px(unit.Dp(44))) rr := inkSize * .5 inkOff := f32.Point{ @@ -97,7 +95,7 @@ func (s SwitchStyle) Layout(gtx layout.Context) layout.Dimensions { stack.Pop() // Set up click area. - stack.Push(gtx.Ops) + stack = op.Push(gtx.Ops) clickSize := gtx.Px(unit.Dp(40)) clickOff := f32.Point{ X: (float32(trackWidth) - float32(clickSize)) * .5, @@ -114,8 +112,7 @@ func (s SwitchStyle) Layout(gtx layout.Context) layout.Dimensions { } func drawDisc(ops *op.Ops, sz float32, col color.RGBA) { - var stack op.StackOp - stack.Push(ops) + defer op.Push(ops).Pop() rr := sz / 2 r := f32.Rectangle{Max: f32.Point{X: sz, Y: sz}} clip.Rect{ @@ -124,5 +121,4 @@ func drawDisc(ops *op.Ops, sz float32, col color.RGBA) { }.Op(ops).Add(ops) paint.ColorOp{Color: col}.Add(ops) paint.PaintOp{Rect: r}.Add(ops) - stack.Pop() }