~eliasnaur/gio

ae8a377cda62204d4c3fdfe0e5b481e1cbeadc41 — Thomas Bruyelle a month ago bade277
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 <thomas.bruyelle@gmail.com>
M io/router/pointer_test.go => io/router/pointer_test.go +1 -4
@@ 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)
}

M layout/flex.go => layout/flex.go +7 -10
@@ 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()

M layout/layout.go => layout/layout.go +3 -6
@@ 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()

M layout/list.go => layout/list.go +4 -7
@@ 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()

M layout/stack.go => layout/stack.go +7 -10
@@ 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()

M op/clip/clip.go => op/clip/clip.go +1 -1
@@ 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.

M op/op.go => op/op.go +21 -6
@@ 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")
	}

M widget/bool.go => widget/bool.go +1 -3
@@ 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}

M widget/button.go => widget/button.go +2 -3
@@ 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 {

M widget/editor.go => widget/editor.go +2 -4
@@ 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{

M widget/enum.go => widget/enum.go +1 -3
@@ 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 {

M widget/image.go => widget/image.go +2 -3
@@ 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}
}

M widget/label.go => widget/label.go +1 -2
@@ 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)

M widget/material/button.go => widget/material/button.go +1 -2
@@ 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))

M widget/material/editor.go => widget/material/editor.go +2 -5
@@ 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
}

M widget/material/switch.go => widget/material/switch.go +6 -10
@@ 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()
}