Files
gio/ui/layout/layout.go
T
Elias Naur 2d8072fbbb ui/layout: replace "interface element" with "widget" in documentation
Now that we have a Widget type, use it in the documentation.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2019-09-22 16:19:07 +02:00

232 lines
4.3 KiB
Go

// SPDX-License-Identifier: Unlicense OR MIT
package layout
import (
"image"
"gioui.org/ui"
)
// Constraints represent a set of acceptable ranges for
// a widget's width and height.
type Constraints struct {
Width Constraint
Height Constraint
}
// Constraint is a range of acceptable sizes in a single
// dimension.
type Constraint struct {
Min, Max int
}
// Dimensions are the resolved size and baseline for a widget.
type Dimensions struct {
Size image.Point
Baseline int
}
// Axis is the Horizontal or Vertical direction.
type Axis uint8
// Alignment is the mutual alignment of a list of widgets.
type Alignment uint8
// Direction is the alignment of widgets relative to a containing
// space.
type Direction uint8
// Widget is a function that computes a set of dimensions that
// satisfies the given constraints.
type Widget func(cs Constraints) Dimensions
const (
Start Alignment = iota
End
Middle
Baseline
)
const (
NW Direction = iota
N
NE
E
SE
S
SW
W
Center
)
const (
Horizontal Axis = iota
Vertical
)
// Constrain a value to the range [Min; Max].
func (c Constraint) Constrain(v int) int {
if v < c.Min {
return c.Min
} else if v > c.Max {
return c.Max
}
return v
}
// Constrain a size to the Width and Height ranges.
func (c Constraints) Constrain(size image.Point) image.Point {
return image.Point{X: c.Width.Constrain(size.X), Y: c.Height.Constrain(size.Y)}
}
// RigidConstraints returns the constraints that can only be
// satisfied by the given dimensions.
func RigidConstraints(size image.Point) Constraints {
return Constraints{
Width: Constraint{Min: size.X, Max: size.X},
Height: Constraint{Min: size.Y, Max: size.Y},
}
}
// Inset adds space around a widget.
type Inset struct {
Top, Right, Bottom, Left ui.Value
}
// Align aligns a widget in the available space.
type Align struct {
Alignment Direction
}
// Layout a widget.
func (in *Inset) Layout(c ui.Config, ops *ui.Ops, cs Constraints, w Widget) Dimensions {
top := c.Px(in.Top)
right := c.Px(in.Right)
bottom := c.Px(in.Bottom)
left := c.Px(in.Left)
mcs := cs
mcs.Width.Min -= left + right
mcs.Width.Max -= left + right
if mcs.Width.Min < 0 {
mcs.Width.Min = 0
}
if mcs.Width.Max < mcs.Width.Min {
mcs.Width.Max = mcs.Width.Min
}
mcs.Height.Min -= top + bottom
mcs.Height.Max -= top + bottom
if mcs.Height.Min < 0 {
mcs.Height.Min = 0
}
if mcs.Height.Max < mcs.Height.Min {
mcs.Height.Max = mcs.Height.Min
}
var stack ui.StackOp
stack.Push(ops)
ui.TransformOp{}.Offset(toPointF(image.Point{X: left, Y: top})).Add(ops)
dims := w(mcs)
stack.Pop()
return Dimensions{
Size: cs.Constrain(dims.Size.Add(image.Point{X: right + left, Y: top + bottom})),
Baseline: dims.Baseline + top,
}
}
// UniformInset returns an Inset with a single inset applied to all
// edges.
func UniformInset(v ui.Value) Inset {
return Inset{Top: v, Right: v, Bottom: v, Left: v}
}
// Layout a widget.
func (a *Align) Layout(ops *ui.Ops, cs Constraints, w Widget) Dimensions {
var macro ui.MacroOp
mcs := cs
mcs.Width.Min = 0
mcs.Height.Min = 0
macro.Record(ops)
dims := w(mcs)
macro.Stop()
sz := dims.Size
if sz.X < cs.Width.Min {
sz.X = cs.Width.Min
}
if sz.Y < cs.Height.Min {
sz.Y = cs.Height.Min
}
var p image.Point
switch a.Alignment {
case N, S, Center:
p.X = (sz.X - dims.Size.X) / 2
case NE, SE, E:
p.X = sz.X - dims.Size.X
}
switch a.Alignment {
case W, Center, E:
p.Y = (sz.Y - dims.Size.Y) / 2
case SW, S, SE:
p.Y = sz.Y - dims.Size.Y
}
var stack ui.StackOp
stack.Push(ops)
ui.TransformOp{}.Offset(toPointF(p)).Add(ops)
macro.Add(ops)
stack.Pop()
return Dimensions{
Size: sz,
Baseline: dims.Baseline,
}
}
func (a Alignment) String() string {
switch a {
case Start:
return "Start"
case End:
return "End"
case Middle:
return "Middle"
case Baseline:
return "Baseline"
default:
panic("unreachable")
}
}
func (a Axis) String() string {
switch a {
case Horizontal:
return "Horizontal"
case Vertical:
return "Vertical"
default:
panic("unreachable")
}
}
func (d Direction) String() string {
switch d {
case NW:
return "NW"
case N:
return "N"
case NE:
return "NE"
case E:
return "E"
case SE:
return "SE"
case S:
return "S"
case SW:
return "SW"
case W:
return "W"
case Center:
return "Center"
default:
panic("unreachable")
}
}