blob: f052552c199fd5fb2985a9ee2fcbe6752f113410 [file] [log] [blame]
// 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"
)
// 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 }
// NewFlow returns a new Flow widget.
func NewFlow(a Axis) Flow {
return Flow{
&Node{
Class: &flowClass{
axis: a,
},
},
}
}
func (o Flow) Axis() Axis { return o.Class.(*flowClass).axis }
func (o Flow) SetAxis(v Axis) { o.Class.(*flowClass).axis = v }
type flowClass struct {
ContainerClassEmbed
axis Axis
}
func (k *flowClass) Measure(n *Node, t *Theme) {
if k.axis != AxisHorizontal && k.axis != AxisVertical {
k.ContainerClassEmbed.Measure(n, t)
return
}
mSize := image.Point{}
for c := n.FirstChild; c != nil; c = c.NextSibling {
c.Measure(t)
if k.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
}
}
}
n.MeasuredSize = mSize
}
func (k *flowClass) Layout(n *Node, t *Theme) {
if k.axis != AxisHorizontal && k.axis != AxisVertical {
k.ContainerClassEmbed.Layout(n, t)
return
}
eaExtra, eaWeight := 0, 0
if k.axis == AxisHorizontal {
eaExtra = n.Rect.Dx()
} else {
eaExtra = n.Rect.Dy()
}
for c := n.FirstChild; c != nil; c = c.NextSibling {
if d, ok := c.LayoutData.(FlowLayoutData); ok && d.ExpandAlongWeight > 0 {
eaWeight += d.ExpandAlongWeight
}
if k.axis == AxisHorizontal {
eaExtra -= c.MeasuredSize.X
} else {
eaExtra -= c.MeasuredSize.Y
}
}
if eaExtra < 0 {
eaExtra = 0
}
p := image.Point{}
for c := n.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 k.axis == AxisHorizontal {
q.X += delta
} else {
q.Y += delta
}
}
if d.ExpandAcross {
if k.axis == AxisHorizontal {
q.Y = max(q.Y, n.Rect.Dy())
} else {
q.X = max(q.X, n.Rect.Dx())
}
}
}
c.Rect = image.Rectangle{
Min: p,
Max: q,
}
c.Layout(t)
if k.axis == AxisHorizontal {
p.X = q.X
} else {
p.Y = q.Y
}
}
}
// 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
// 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
// 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.
ExpandAcross bool
}