diff --git a/shiny/example/layout/main.go b/shiny/example/layout/main.go index fcf1a3c..b5cac9d 100644 --- a/shiny/example/layout/main.go +++ b/shiny/example/layout/main.go @@ -46,17 +46,17 @@ func main() { widget.NewLabel("Cyan:"), widget.WithLayoutData( colorPatch(color.RGBA{0x00, 0x7f, 0x7f, 0xff}, px(0), px(20)), - widget.FlowLayoutData{ExpandAlongWeight: 1}, + widget.FlowLayoutData{AlongWeight: 1, ExpandAlong: true}, ), widget.NewLabel("Magenta:"), widget.WithLayoutData( colorPatch(color.RGBA{0x7f, 0x00, 0x7f, 0xff}, px(0), px(30)), - widget.FlowLayoutData{ExpandAlongWeight: 2}, + widget.FlowLayoutData{AlongWeight: 2, ExpandAlong: true}, ), widget.NewLabel("Yellow:"), widget.WithLayoutData( colorPatch(color.RGBA{0x7f, 0x7f, 0x00, 0xff}, px(0), px(40)), - widget.FlowLayoutData{ExpandAlongWeight: 3}, + widget.FlowLayoutData{AlongWeight: 3, ExpandAlong: true}, ), ) diff --git a/shiny/example/textedit/main.go b/shiny/example/textedit/main.go index de8929a..b47ba17 100644 --- a/shiny/example/textedit/main.go +++ b/shiny/example/textedit/main.go @@ -25,10 +25,13 @@ import ( "golang.org/x/exp/shiny/widget/theme" ) -func expand(n node.Node, expandAlongWeight int) node.Node { +func stretch(n node.Node, alongWeight int) node.Node { return widget.WithLayoutData(n, widget.FlowLayoutData{ - ExpandAcross: true, - ExpandAlongWeight: expandAlongWeight, + AlongWeight: alongWeight, + ExpandAlong: true, + ShrinkAlong: true, + ExpandAcross: true, + ShrinkAcross: true, }) } @@ -39,7 +42,7 @@ func main() { widget.NewPadder(widget.AxisBoth, unit.Ems(0.5), widget.NewFlow(widget.AxisHorizontal, widget.NewLabel("TODO: status"), - expand(widget.NewSpace(), 1), + stretch(widget.NewSpace(), 1), widget.NewLabel("TODO: Menu"), ), ), @@ -50,10 +53,10 @@ func main() { body := widget.NewText(prideAndPrejudice) w := widget.NewFlow(widget.AxisVertical, - expand(widget.NewSheet(header), 0), - expand(widget.NewSheet(divider), 0), + stretch(widget.NewSheet(header), 0), + stretch(widget.NewSheet(divider), 0), // TODO: make the body's sheet scrollable. - expand(widget.NewSheet(body), 1), + stretch(widget.NewSheet(body), 1), ) if err := widget.RunWindow(s, w, nil); err != nil { diff --git a/shiny/widget/flow.go b/shiny/widget/flow.go index 29c85d1..9dcc5a2 100644 --- a/shiny/widget/flow.go +++ b/shiny/widget/flow.go @@ -71,46 +71,73 @@ func (w *Flow) Layout(t *theme.Theme) { return } - eaExtra, eaWeight := 0, 0 + extra, totalExpandWeight, totalShrinkWeight := 0, 0, 0 if w.Axis == AxisHorizontal { - eaExtra = w.Rect.Dx() + extra = w.Rect.Dx() } else { - eaExtra = w.Rect.Dy() + extra = w.Rect.Dy() } for c := w.FirstChild; c != nil; c = c.NextSibling { - if d, ok := c.LayoutData.(FlowLayoutData); ok && d.ExpandAlongWeight > 0 { - eaWeight += d.ExpandAlongWeight + if d, ok := c.LayoutData.(FlowLayoutData); ok && d.AlongWeight > 0 { + if d.AlongWeight <= 0 { + continue + } + if d.ExpandAlong { + totalExpandWeight += d.AlongWeight + } + if d.ShrinkAlong { + totalShrinkWeight += d.AlongWeight + } } if w.Axis == AxisHorizontal { - eaExtra -= c.MeasuredSize.X + extra -= c.MeasuredSize.X } else { - eaExtra -= c.MeasuredSize.Y + extra -= c.MeasuredSize.Y } } - if eaExtra < 0 { - eaExtra = 0 + expand, shrink, totalWeight := extra > 0, extra < 0, 0 + if expand { + if totalExpandWeight == 0 { + expand = false + } else { + totalWeight = totalExpandWeight + } + } + if shrink { + if totalShrinkWeight == 0 { + shrink = false + } else { + totalWeight = totalShrinkWeight + } } p := image.Point{} for c := w.FirstChild; c != nil; c = c.NextSibling { q := p.Add(c.MeasuredSize) if d, ok := c.LayoutData.(FlowLayoutData); ok { - if d.ExpandAlongWeight > 0 { - delta := eaExtra * d.ExpandAlongWeight / eaWeight - eaExtra -= delta - eaWeight -= d.ExpandAlongWeight - if w.Axis == AxisHorizontal { - q.X += delta - } else { - q.Y += delta + if d.AlongWeight > 0 { + if (expand && d.ExpandAlong) || (shrink && d.ShrinkAlong) { + delta := extra * d.AlongWeight / totalWeight + extra -= delta + totalWeight -= d.AlongWeight + if w.Axis == AxisHorizontal { + q.X += delta + if q.X < p.X { + q.X = p.X + } + } else { + q.Y += delta + if q.Y < p.Y { + q.Y = p.Y + } + } } } - if d.ExpandAcross { - if w.Axis == AxisHorizontal { - q.Y = max(q.Y, w.Rect.Dy()) - } else { - q.X = max(q.X, w.Rect.Dx()) - } + + if w.Axis == AxisHorizontal { + q.Y = stretchAcross(q.Y, w.Rect.Dy(), d.ExpandAcross, d.ShrinkAcross) + } else { + q.X = stretchAcross(q.X, w.Rect.Dx(), d.ExpandAcross, d.ShrinkAcross) } } c.Rect = image.Rectangle{ @@ -126,19 +153,55 @@ func (w *Flow) Layout(t *theme.Theme) { } } +func stretchAcross(child, parent int, expand, shrink bool) int { + if (expand && child < parent) || (shrink && child > parent) { + return parent + } + return child +} + // FlowLayoutData is the node LayoutData type for a Flow's children. type FlowLayoutData struct { - // ExpandAlongWeight is the relative weight for distributing any excess - // space along the Flow's axis. For example, if an AxisHorizontal Flow's + // AlongWeight is the relative weight for distributing any space surplus or + // deficit along the Flow's axis. For example, if an AxisHorizontal Flow's // Rect width was 100 pixels greater than the sum of its children's natural - // widths, and three children had non-zero FlowLayoutData.ExpandAlongWeight - // values 6, 3 and 1, then those children's laid out widths would be larger - // than their natural widths by 60, 30 and 10 pixels. - ExpandAlongWeight int + // widths, and three children had non-zero FlowLayoutData.AlongWeight + // values 6, 3 and 1 (and their FlowLayoutData.ExpandAlong values were + // true) then those children's laid out widths would be larger than their + // natural widths by 60, 30 and 10 pixels. + // + // A negative AlongWeight is equivalent to zero. + AlongWeight int - // ExpandAcross is whether the child's laid out size should expand to fill - // the Flow's cross-axis. For example, if an AxisHorizontal Flow's Rect - // height was 80 pixels, any child whose FlowLayoutData.ExpandAcross was - // true would also be laid out with at least an 80 pixel height. + // ExpandAlong is whether the child's laid out size should increase along + // the Flow's axis, based on AlongWeight, if there is a space surplus (the + // children's measured size total less than the parent's size). To allow + // size decreases as well as increases, set ShrinkAlong. + ExpandAlong bool + + // ShrinkAlong is whether the child's laid out size should decrease along + // the Flow's axis, based on AlongWeight, if there is a space deficit (the + // children's measured size total more than the parent's size). To allow + // size increases as well as decreases, set ExpandAlong. + ShrinkAlong bool + + // ExpandAcross is whether the child's laid out size should increase along + // the Flow's cross-axis if there is a space surplus (the child's measured + // size is less than the parent's size). To allow size decreases as well as + // increases, set ShrinkAcross. + // + // For example, if an AxisHorizontal Flow's Rect height was 80 pixels, any + // child whose FlowLayoutData.ExpandAcross was true would also be laid out + // with at least an 80 pixel height. ExpandAcross bool + + // ShrinkAcross is whether the child's laid out size should decrease along + // the Flow's cross-axis if there is a space deficit (the child's measured + // size is more than the parent's size). To allow size increases as well as + // decreases, set ExpandAcross. + // + // For example, if an AxisHorizontal Flow's Rect height was 80 pixels, any + // child whose FlowLayoutData.ShrinkAcross was true would also be laid out + // with at most an 80 pixel height. + ShrinkAcross bool } diff --git a/shiny/widget/widget.go b/shiny/widget/widget.go index d45108d..0dcbf63 100644 --- a/shiny/widget/widget.go +++ b/shiny/widget/widget.go @@ -22,13 +22,6 @@ import ( "golang.org/x/mobile/event/size" ) -func max(x, y int) int { - if x > y { - return x - } - return y -} - // Axis is zero, one or both of the horizontal and vertical axes. For example, // a widget may be scrollable in one of the four AxisXxx values. type Axis uint8