1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
|
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package widget
import (
"image"
"golang.org/x/exp/shiny/widget/node"
"golang.org/x/exp/shiny/widget/theme"
)
// TODO: padding, alignment.
// Flow is a container widget that lays out its children in sequence along an
// axis, either horizontally or vertically. The children's laid out size may
// differ from their natural size, along or across that axis, if a child's
// LayoutData is a FlowLayoutData.
type Flow struct {
node.ContainerEmbed
Axis Axis
}
// NewFlow returns a new Flow widget containing the given children.
func NewFlow(a Axis, children ...node.Node) *Flow {
w := &Flow{
Axis: a,
}
w.Wrapper = w
for _, c := range children {
w.Insert(c, nil)
}
return w
}
func (w *Flow) Measure(t *theme.Theme, widthHint, heightHint int) {
if w.Axis != AxisHorizontal && w.Axis != AxisVertical {
w.ContainerEmbed.Measure(t, widthHint, heightHint)
return
}
if w.Axis == AxisHorizontal {
widthHint = node.NoHint
}
if w.Axis == AxisVertical {
heightHint = node.NoHint
}
mSize := image.Point{}
for c := w.FirstChild; c != nil; c = c.NextSibling {
c.Wrapper.Measure(t, widthHint, heightHint)
if w.Axis == AxisHorizontal {
mSize.X += c.MeasuredSize.X
if mSize.Y < c.MeasuredSize.Y {
mSize.Y = c.MeasuredSize.Y
}
} else {
mSize.Y += c.MeasuredSize.Y
if mSize.X < c.MeasuredSize.X {
mSize.X = c.MeasuredSize.X
}
}
}
w.MeasuredSize = mSize
}
func (w *Flow) Layout(t *theme.Theme) {
if w.Axis != AxisHorizontal && w.Axis != AxisVertical {
w.ContainerEmbed.Layout(t)
return
}
extra, totalExpandWeight, totalShrinkWeight := 0, 0, 0
if w.Axis == AxisHorizontal {
extra = w.Rect.Dx()
} else {
extra = w.Rect.Dy()
}
for c := w.FirstChild; c != nil; c = c.NextSibling {
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 {
extra -= c.MeasuredSize.X
} else {
extra -= c.MeasuredSize.Y
}
}
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.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 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{
Min: p,
Max: q,
}
c.Wrapper.Layout(t)
if w.Axis == AxisHorizontal {
p.X = q.X
} else {
p.Y = q.Y
}
}
}
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 {
// 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.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
// 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
}
|