shiny/widget: let a Flow shrink as well as expand.

This will be needed for scrollable Sheets, where the parent (the Sheet)
should be laid out at a smaller size than its child.

Change-Id: Iacf69c90f263f24f9fd149f3864d23737c1593a8
Reviewed-on: https://go-review.googlesource.com/28345
Reviewed-by: David Crawshaw <crawshaw@golang.org>
This commit is contained in:
Nigel Tao 2016-09-01 16:06:57 +10:00
Родитель da927ba5e2
Коммит 14f0c66cee
4 изменённых файлов: 109 добавлений и 50 удалений

Просмотреть файл

@ -46,17 +46,17 @@ func main() {
widget.NewLabel("Cyan:"), widget.NewLabel("Cyan:"),
widget.WithLayoutData( widget.WithLayoutData(
colorPatch(color.RGBA{0x00, 0x7f, 0x7f, 0xff}, px(0), px(20)), 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.NewLabel("Magenta:"),
widget.WithLayoutData( widget.WithLayoutData(
colorPatch(color.RGBA{0x7f, 0x00, 0x7f, 0xff}, px(0), px(30)), 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.NewLabel("Yellow:"),
widget.WithLayoutData( widget.WithLayoutData(
colorPatch(color.RGBA{0x7f, 0x7f, 0x00, 0xff}, px(0), px(40)), colorPatch(color.RGBA{0x7f, 0x7f, 0x00, 0xff}, px(0), px(40)),
widget.FlowLayoutData{ExpandAlongWeight: 3}, widget.FlowLayoutData{AlongWeight: 3, ExpandAlong: true},
), ),
) )

Просмотреть файл

@ -25,10 +25,13 @@ import (
"golang.org/x/exp/shiny/widget/theme" "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{ return widget.WithLayoutData(n, widget.FlowLayoutData{
ExpandAcross: true, AlongWeight: alongWeight,
ExpandAlongWeight: expandAlongWeight, ExpandAlong: true,
ShrinkAlong: true,
ExpandAcross: true,
ShrinkAcross: true,
}) })
} }
@ -39,7 +42,7 @@ func main() {
widget.NewPadder(widget.AxisBoth, unit.Ems(0.5), widget.NewPadder(widget.AxisBoth, unit.Ems(0.5),
widget.NewFlow(widget.AxisHorizontal, widget.NewFlow(widget.AxisHorizontal,
widget.NewLabel("TODO: status"), widget.NewLabel("TODO: status"),
expand(widget.NewSpace(), 1), stretch(widget.NewSpace(), 1),
widget.NewLabel("TODO: Menu"), widget.NewLabel("TODO: Menu"),
), ),
), ),
@ -50,10 +53,10 @@ func main() {
body := widget.NewText(prideAndPrejudice) body := widget.NewText(prideAndPrejudice)
w := widget.NewFlow(widget.AxisVertical, w := widget.NewFlow(widget.AxisVertical,
expand(widget.NewSheet(header), 0), stretch(widget.NewSheet(header), 0),
expand(widget.NewSheet(divider), 0), stretch(widget.NewSheet(divider), 0),
// TODO: make the body's sheet scrollable. // 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 { if err := widget.RunWindow(s, w, nil); err != nil {

Просмотреть файл

@ -71,46 +71,73 @@ func (w *Flow) Layout(t *theme.Theme) {
return return
} }
eaExtra, eaWeight := 0, 0 extra, totalExpandWeight, totalShrinkWeight := 0, 0, 0
if w.Axis == AxisHorizontal { if w.Axis == AxisHorizontal {
eaExtra = w.Rect.Dx() extra = w.Rect.Dx()
} else { } else {
eaExtra = w.Rect.Dy() extra = w.Rect.Dy()
} }
for c := w.FirstChild; c != nil; c = c.NextSibling { for c := w.FirstChild; c != nil; c = c.NextSibling {
if d, ok := c.LayoutData.(FlowLayoutData); ok && d.ExpandAlongWeight > 0 { if d, ok := c.LayoutData.(FlowLayoutData); ok && d.AlongWeight > 0 {
eaWeight += d.ExpandAlongWeight if d.AlongWeight <= 0 {
continue
}
if d.ExpandAlong {
totalExpandWeight += d.AlongWeight
}
if d.ShrinkAlong {
totalShrinkWeight += d.AlongWeight
}
} }
if w.Axis == AxisHorizontal { if w.Axis == AxisHorizontal {
eaExtra -= c.MeasuredSize.X extra -= c.MeasuredSize.X
} else { } else {
eaExtra -= c.MeasuredSize.Y extra -= c.MeasuredSize.Y
} }
} }
if eaExtra < 0 { expand, shrink, totalWeight := extra > 0, extra < 0, 0
eaExtra = 0 if expand {
if totalExpandWeight == 0 {
expand = false
} else {
totalWeight = totalExpandWeight
}
}
if shrink {
if totalShrinkWeight == 0 {
shrink = false
} else {
totalWeight = totalShrinkWeight
}
} }
p := image.Point{} p := image.Point{}
for c := w.FirstChild; c != nil; c = c.NextSibling { for c := w.FirstChild; c != nil; c = c.NextSibling {
q := p.Add(c.MeasuredSize) q := p.Add(c.MeasuredSize)
if d, ok := c.LayoutData.(FlowLayoutData); ok { if d, ok := c.LayoutData.(FlowLayoutData); ok {
if d.ExpandAlongWeight > 0 { if d.AlongWeight > 0 {
delta := eaExtra * d.ExpandAlongWeight / eaWeight if (expand && d.ExpandAlong) || (shrink && d.ShrinkAlong) {
eaExtra -= delta delta := extra * d.AlongWeight / totalWeight
eaWeight -= d.ExpandAlongWeight extra -= delta
if w.Axis == AxisHorizontal { totalWeight -= d.AlongWeight
q.X += delta if w.Axis == AxisHorizontal {
} else { q.X += delta
q.Y += 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 { if w.Axis == AxisHorizontal {
q.Y = max(q.Y, w.Rect.Dy()) q.Y = stretchAcross(q.Y, w.Rect.Dy(), d.ExpandAcross, d.ShrinkAcross)
} else { } else {
q.X = max(q.X, w.Rect.Dx()) q.X = stretchAcross(q.X, w.Rect.Dx(), d.ExpandAcross, d.ShrinkAcross)
}
} }
} }
c.Rect = image.Rectangle{ 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. // FlowLayoutData is the node LayoutData type for a Flow's children.
type FlowLayoutData struct { type FlowLayoutData struct {
// ExpandAlongWeight is the relative weight for distributing any excess // AlongWeight is the relative weight for distributing any space surplus or
// space along the Flow's axis. For example, if an AxisHorizontal Flow's // 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 // Rect width was 100 pixels greater than the sum of its children's natural
// widths, and three children had non-zero FlowLayoutData.ExpandAlongWeight // widths, and three children had non-zero FlowLayoutData.AlongWeight
// values 6, 3 and 1, then those children's laid out widths would be larger // values 6, 3 and 1 (and their FlowLayoutData.ExpandAlong values were
// than their natural widths by 60, 30 and 10 pixels. // true) then those children's laid out widths would be larger than their
ExpandAlongWeight int // 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 // ExpandAlong is whether the child's laid out size should increase along
// the Flow's cross-axis. For example, if an AxisHorizontal Flow's Rect // the Flow's axis, based on AlongWeight, if there is a space surplus (the
// height was 80 pixels, any child whose FlowLayoutData.ExpandAcross was // children's measured size total less than the parent's size). To allow
// true would also be laid out with at least an 80 pixel height. // 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 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
} }

Просмотреть файл

@ -22,13 +22,6 @@ import (
"golang.org/x/mobile/event/size" "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, // 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. // a widget may be scrollable in one of the four AxisXxx values.
type Axis uint8 type Axis uint8