diff --git a/layout/example_test.go b/layout/example_test.go index 0cc59cf0..86174292 100644 --- a/layout/example_test.go +++ b/layout/example_test.go @@ -53,21 +53,18 @@ func ExampleFlex() { gtx := new(layout.Context) gtx.Reset(nil, image.Point{X: 100, Y: 100}) - flex := layout.Flex{} - - // Rigid 10x10 widget. - child1 := flex.Rigid(gtx, func() { - fmt.Printf("Rigid: %v\n", gtx.Constraints.Width) - layoutWidget(gtx, 10, 10) - }) - - // Child with 50% space allowance. - child2 := flex.Flex(gtx, 0.5, func() { - fmt.Printf("50%%: %v\n", gtx.Constraints.Width) - layoutWidget(gtx, 10, 10) - }) - - flex.Layout(gtx, child1, child2) + layout.Flex{}.Layout(gtx, + // Rigid 10x10 widget. + layout.Rigid(func() { + fmt.Printf("Rigid: %v\n", gtx.Constraints.Width) + layoutWidget(gtx, 10, 10) + }), + // Child with 50% space allowance. + layout.Flexed(0.5, func() { + fmt.Printf("50%%: %v\n", gtx.Constraints.Width) + layoutWidget(gtx, 10, 10) + }), + ) // Output: // Rigid: {0 100} diff --git a/layout/flex.go b/layout/flex.go index af65a3eb..71d36533 100644 --- a/layout/flex.go +++ b/layout/flex.go @@ -19,20 +19,16 @@ type Flex struct { Spacing Spacing // Alignment is the alignment in the cross axis. Alignment Alignment - - size int - 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. +// FlexChild is the descriptor for a Flex child. type FlexChild struct { + flex bool + weight float32 + + widget Widget + + // Scratch space. macro op.MacroOp dims Dimensions } @@ -60,74 +56,82 @@ const ( SpaceEvenly ) -// 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 - if mainMax < 0 { - mainMax = 0 +// Rigid returns a Flex child with a maximal constraint of the +// remaining space. +func Rigid(widget Widget) FlexChild { + return FlexChild{ + widget: widget, } - cs = axisConstraints(f.Axis, Constraint{Max: mainMax}, axisCrossConstraint(f.Axis, cs)) - var m op.MacroOp - m.Record(gtx.Ops) - dims := ctxLayout(gtx, cs, w) - m.Stop() - f.rigidSize += axisMain(f.Axis, dims.Size) - f.expand(dims) - return FlexChild{m, dims} } -func (f *Flex) begin(ops *op.Ops) { - if f.begun { - return +// Flexed returns a Flex child forced to take up a fraction of +// the remaining space. +func Flexed(weight float32, widget Widget) FlexChild { + return FlexChild{ + flex: true, + weight: weight, + widget: widget, } - 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 - if mainc.Max > f.size { - flexSize = mainc.Max - f.rigidSize - // Apply weight and add any leftover fraction from a - // previous Flex. - size := float32(flexSize)*weight + f.fraction - flexSize = int(size + .5) - f.fraction = size - float32(flexSize) - if max := mainc.Max - f.size; flexSize > max { - flexSize = max +// Layout a list of children. The position of the children are +// determined by the specified order, but Rigid children are laid out +// before Flexed children. +func (f Flex) Layout(gtx *Context, children ...FlexChild) { + size := 0 + // Lay out Rigid children. + for i, child := range children { + if child.flex { + continue } + cs := gtx.Constraints + mainc := axisMainConstraint(f.Axis, cs) + mainMax := mainc.Max - size + if mainMax < 0 { + mainMax = 0 + } + cs = axisConstraints(f.Axis, Constraint{Max: mainMax}, axisCrossConstraint(f.Axis, cs)) + var m op.MacroOp + m.Record(gtx.Ops) + dims := ctxLayout(gtx, cs, child.widget) + m.Stop() + sz := axisMain(f.Axis, dims.Size) + size += sz + children[i].macro = m + children[i].dims = dims } - submainc := Constraint{Min: flexSize, Max: flexSize} - cs = axisConstraints(f.Axis, submainc, axisCrossConstraint(f.Axis, cs)) - var m op.MacroOp - m.Record(gtx.Ops) - dims := ctxLayout(gtx, cs, w) - m.Stop() - f.expand(dims) - return FlexChild{m, dims} -} - -// End a child by specifying its dimensions. Pass the returned layout result -// to Layout. -func (f *Flex) expand(dims Dimensions) { - sz := axisMain(f.Axis, dims.Size) - f.size += sz -} - -// 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() + rigidSize := size + // fraction is the rounding error from a Flex weighting. + var fraction float32 + // Lay out Flexed children. + for i, child := range children { + if !child.flex { + continue + } + cs := gtx.Constraints + mainc := axisMainConstraint(f.Axis, cs) + var flexSize int + if mainc.Max > size { + flexSize = mainc.Max - rigidSize + // Apply weight and add any leftover fraction from a + // previous Flexed. + childSize := float32(flexSize)*child.weight + fraction + flexSize = int(childSize + .5) + fraction = childSize - float32(flexSize) + if max := mainc.Max - size; flexSize > max { + flexSize = max + } + } + submainc := Constraint{Min: flexSize, Max: flexSize} + cs = axisConstraints(f.Axis, submainc, axisCrossConstraint(f.Axis, cs)) + var m op.MacroOp + m.Record(gtx.Ops) + dims := ctxLayout(gtx, cs, child.widget) + m.Stop() + sz := axisMain(f.Axis, dims.Size) + size += sz + children[i].macro = m + children[i].dims = dims } var maxCross int var maxBaseline int @@ -142,8 +146,8 @@ func (f *Flex) Layout(gtx *Context, children ...FlexChild) { cs := gtx.Constraints mainc := axisMainConstraint(f.Axis, cs) var space int - if mainc.Min > f.size { - space = mainc.Min - f.size + if mainc.Min > size { + space = mainc.Min - size } var mainSize int switch f.Spacing { @@ -199,9 +203,6 @@ 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 } func axisPoint(a Axis, main, cross int) image.Point { diff --git a/widget/material/checkable.go b/widget/material/checkable.go index 1137b8a4..53c5eaff 100644 --- a/widget/material/checkable.go +++ b/widget/material/checkable.go @@ -36,32 +36,30 @@ func (c *checkable) layout(gtx *layout.Context, checked bool) { hmin := gtx.Constraints.Width.Min vmin := gtx.Constraints.Height.Min - flex := layout.Flex{Alignment: layout.Middle} - - ico := flex.Rigid(gtx, func() { - layout.Align(layout.Center).Layout(gtx, func() { - layout.UniformInset(unit.Dp(2)).Layout(gtx, func() { - size := gtx.Px(c.Size) - icon.Color = c.IconColor - icon.Layout(gtx, unit.Px(float32(size))) - gtx.Dimensions = layout.Dimensions{ - Size: image.Point{X: size, Y: size}, - } + layout.Flex{Alignment: layout.Middle}.Layout(gtx, + layout.Rigid(func() { + layout.Align(layout.Center).Layout(gtx, func() { + layout.UniformInset(unit.Dp(2)).Layout(gtx, func() { + size := gtx.Px(c.Size) + icon.Color = c.IconColor + icon.Layout(gtx, unit.Px(float32(size))) + gtx.Dimensions = layout.Dimensions{ + Size: image.Point{X: size, Y: size}, + } + }) }) - }) - }) + }), - lbl := flex.Rigid(gtx, func() { - gtx.Constraints.Width.Min = hmin - gtx.Constraints.Height.Min = vmin - layout.Align(layout.Start).Layout(gtx, func() { - layout.UniformInset(unit.Dp(2)).Layout(gtx, func() { - paint.ColorOp{Color: c.Color}.Add(gtx.Ops) - widget.Label{}.Layout(gtx, c.shaper, c.Font, c.Label) + layout.Rigid(func() { + gtx.Constraints.Width.Min = hmin + gtx.Constraints.Height.Min = vmin + layout.Align(layout.Start).Layout(gtx, func() { + layout.UniformInset(unit.Dp(2)).Layout(gtx, func() { + paint.ColorOp{Color: c.Color}.Add(gtx.Ops) + widget.Label{}.Layout(gtx, c.shaper, c.Font, c.Label) + }) }) - }) - }) - - flex.Layout(gtx, ico, lbl) + }), + ) pointer.Rect(image.Rectangle{Max: gtx.Dimensions.Size}).Add(gtx.Ops) }