~eliasnaur/gio

65dc6797ebb9ec7ba46eceedc025895c296d6e44 — Elias Naur 8 months ago 8102f63
layout: don't allow overlapping Flex and Stack layouts

Overlapping layouts such as

	outer := layout.Flex{}
	inner := layout.Stack{}
	child := inner.Rigid(gtx, ...)
	outerChild := outer.Rigid(gtx, func() {
		inner.Layout(gtx, child)
	})
	outer.Layout(gtx, outerChild)

runs but result in a wrong layout.

This change use empty StackOps to ensure that the Stack and Flex
child layout methods are called in the same scope as their Layout
methods:

	outer := layout.Flex{}
	inner := layout.Stack{}
	outerChild := outer.Rigid(gtx, func() {
		child := inner.Rigid(gtx, ...)
		inner.Layout(gtx, child)
	})
	outer.Layout(gtx, outerChild)

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2 files changed, 34 insertions(+), 3 deletions(-)

M layout/flex.go
M layout/stack.go
M layout/flex.go => layout/flex.go +19 -0
@@ 24,6 24,11 @@ type Flex struct {
	rigidSize int
	// fraction is the rounding error from a Flex weighting.
	fraction float32

	// Use an empty StackOp for tracking whether Rigid, Flex
	// is called in the same layout scope as Layout.
	begun bool
	stack op.StackOp
}

// FlexChild is the layout result of a call End.


@@ 58,6 63,7 @@ const (
// Rigid lays out a widget with the main axis constrained to the range
// from 0 to the remaining space.
func (f *Flex) Rigid(gtx *Context, w Widget) FlexChild {
	f.begin(gtx.Ops)
	cs := gtx.Constraints
	mainc := axisMainConstraint(f.Axis, cs)
	mainMax := mainc.Max - f.size


@@ 74,9 80,18 @@ func (f *Flex) Rigid(gtx *Context, w Widget) FlexChild {
	return FlexChild{m, dims}
}

func (f *Flex) begin(ops *op.Ops) {
	if f.begun {
		return
	}
	f.stack.Push(ops)
	f.begun = true
}

// Flex is like Rigid, where the main axis size is also constrained to a
// fraction of the space not taken up by Rigid children.
func (f *Flex) Flex(gtx *Context, weight float32, w Widget) FlexChild {
	f.begin(gtx.Ops)
	cs := gtx.Constraints
	mainc := axisMainConstraint(f.Axis, cs)
	var flexSize int


@@ 111,6 126,9 @@ func (f *Flex) expand(dims Dimensions) {
// Layout a list of children. The order of the children determines their laid
// out order.
func (f *Flex) Layout(gtx *Context, children ...FlexChild) {
	if len(children) > 0 {
		f.stack.Pop()
	}
	var maxCross int
	var maxBaseline int
	for _, child := range children {


@@ 181,6 199,7 @@ func (f *Flex) Layout(gtx *Context, children ...FlexChild) {
	}
	sz := axisPoint(f.Axis, mainSize, maxCross)
	gtx.Dimensions = Dimensions{Size: sz, Baseline: sz.Y - maxBaseline}
	f.begun = false
	f.size = 0
	f.rigidSize = 0
}

M layout/stack.go => layout/stack.go +15 -3
@@ 16,6 16,10 @@ type Stack struct {
	Alignment Direction

	maxSZ image.Point
	// Use an empty StackOp for tracking whether Rigid, Flex
	// is called in the same layout scope as Layout.
	begun bool
	stack op.StackOp
}

// StackChild is the layout result of a call to End.


@@ 34,7 38,7 @@ func (s *Stack) Rigid(gtx *Context, w Widget) StackChild {
	m.Record(gtx.Ops)
	dims := ctxLayout(gtx, cs, w)
	m.Stop()
	s.expand(dims)
	s.expand(gtx.Ops, dims)
	return StackChild{m, dims}
}



@@ 48,11 52,15 @@ func (s *Stack) Expand(gtx *Context, w Widget) StackChild {
	}
	dims := ctxLayout(gtx, cs, w)
	m.Stop()
	s.expand(dims)
	s.expand(gtx.Ops, dims)
	return StackChild{m, dims}
}

func (s *Stack) expand(dims Dimensions) {
func (s *Stack) expand(ops *op.Ops, dims Dimensions) {
	if !s.begun {
		s.stack.Push(ops)
		s.begun = true
	}
	if w := dims.Size.X; w > s.maxSZ.X {
		s.maxSZ.X = w
	}


@@ 64,8 72,12 @@ func (s *Stack) expand(dims Dimensions) {
// Layout a list of children. The order of the children determines their laid
// out order.
func (s *Stack) Layout(gtx *Context, children ...StackChild) {
	if len(children) > 0 {
		s.stack.Pop()
	}
	maxSZ := gtx.Constraints.Constrain(s.maxSZ)
	s.maxSZ = image.Point{}
	s.begun = false
	var baseline int
	for _, ch := range children {
		sz := ch.dims.Size