mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
9b3429d6da
The layout package switched from interfaces to functions for composing layouts. The switch made sure that no garbage is generated for transient layouts such as Align, Inset, Stack, Flex. Unfortunately, that left the stateful widgets and layouts: as soon as their layout methods are embedded in a transient layout, a closure is generated that escapes to the heap. To avoid garbage for both transient as well as stateful widgets, replace the functional approach with explicit begin/end methods. A begin method generally starts an op block and returns the adjusted constraints. An end method takes computed dimensions, ends its op block and returns adjusted dimensions. Signed-off-by: Elias Naur <mail@eliasnaur.com>
188 lines
3.8 KiB
Go
188 lines
3.8 KiB
Go
// SPDX-License-Identifier: Unlicense OR MIT
|
|
|
|
package layout
|
|
|
|
import (
|
|
"image"
|
|
"math"
|
|
|
|
"gioui.org/ui"
|
|
)
|
|
|
|
type Constraints struct {
|
|
Width Constraint
|
|
Height Constraint
|
|
}
|
|
|
|
type Constraint struct {
|
|
Min, Max int
|
|
}
|
|
|
|
type Dimens struct {
|
|
Size image.Point
|
|
Baseline int
|
|
}
|
|
|
|
type Axis uint8
|
|
|
|
const (
|
|
Horizontal Axis = iota
|
|
Vertical
|
|
)
|
|
|
|
func (c Constraint) Constrain(v int) int {
|
|
if v < c.Min {
|
|
return c.Min
|
|
} else if v > c.Max {
|
|
return c.Max
|
|
}
|
|
return v
|
|
}
|
|
|
|
func (c Constraints) Constrain(p image.Point) image.Point {
|
|
return image.Point{X: c.Width.Constrain(p.X), Y: c.Height.Constrain(p.Y)}
|
|
}
|
|
|
|
func (c Constraints) Expand() Constraints {
|
|
return Constraints{Width: c.Width.Expand(), Height: c.Height.Expand()}
|
|
}
|
|
|
|
func (c Constraint) Expand() Constraint {
|
|
return Constraint{Min: c.Max, Max: c.Max}
|
|
}
|
|
func (c Constraints) Loose() Constraints {
|
|
return Constraints{Width: c.Width.Loose(), Height: c.Height.Loose()}
|
|
}
|
|
|
|
func (c Constraint) Loose() Constraint {
|
|
return Constraint{Max: c.Max}
|
|
}
|
|
|
|
// ExactConstraints returns the constraints that exactly represents the
|
|
// given dimensions.
|
|
func ExactConstraints(size image.Point) Constraints {
|
|
return Constraints{
|
|
Width: Constraint{Min: size.X, Max: size.X},
|
|
Height: Constraint{Min: size.Y, Max: size.Y},
|
|
}
|
|
}
|
|
|
|
type Insets struct {
|
|
Top, Right, Bottom, Left float32
|
|
|
|
cs Constraints
|
|
}
|
|
|
|
func (in *Insets) Begin(ops *ui.Ops, cs Constraints) Constraints {
|
|
in.cs = cs
|
|
mcs := cs
|
|
t, r, b, l := int(math.Round(float64(in.Top))), int(math.Round(float64(in.Right))), int(math.Round(float64(in.Bottom))), int(math.Round(float64(in.Left)))
|
|
if mcs.Width.Max != ui.Inf {
|
|
mcs.Width.Min -= l + r
|
|
mcs.Width.Max -= l + r
|
|
if mcs.Width.Min < 0 {
|
|
mcs.Width.Min = 0
|
|
}
|
|
if mcs.Width.Max < mcs.Width.Min {
|
|
mcs.Width.Max = mcs.Width.Min
|
|
}
|
|
}
|
|
if mcs.Height.Max != ui.Inf {
|
|
mcs.Height.Min -= t + b
|
|
mcs.Height.Max -= t + b
|
|
if mcs.Height.Min < 0 {
|
|
mcs.Height.Min = 0
|
|
}
|
|
if mcs.Height.Max < mcs.Height.Min {
|
|
mcs.Height.Max = mcs.Height.Min
|
|
}
|
|
}
|
|
ops.Begin()
|
|
ui.OpTransform{Transform: ui.Offset(toPointF(image.Point{X: l, Y: t}))}.Add(ops)
|
|
return mcs
|
|
}
|
|
|
|
func (in *Insets) End(ops *ui.Ops, dims Dimens) Dimens {
|
|
ops.End().Add(ops)
|
|
t, r, b, l := int(math.Round(float64(in.Top))), int(math.Round(float64(in.Right))), int(math.Round(float64(in.Bottom))), int(math.Round(float64(in.Left)))
|
|
return Dimens{
|
|
Size: in.cs.Constrain(dims.Size.Add(image.Point{X: r + l, Y: t + b})),
|
|
Baseline: dims.Baseline + t,
|
|
}
|
|
}
|
|
|
|
func EqualInsets(v float32) Insets {
|
|
return Insets{Top: v, Right: v, Bottom: v, Left: v}
|
|
}
|
|
|
|
func isInf(v ui.Value) bool {
|
|
return math.IsInf(float64(v.V), 1)
|
|
}
|
|
|
|
type Sized struct {
|
|
Width, Height float32
|
|
}
|
|
|
|
func (s Sized) Constrain(cs Constraints) Constraints {
|
|
if h := int(s.Height + 0.5); h != 0 {
|
|
if cs.Height.Min < h {
|
|
cs.Height.Min = h
|
|
}
|
|
if h < cs.Height.Max {
|
|
cs.Height.Max = h
|
|
}
|
|
}
|
|
if w := int(s.Width + .5); w != 0 {
|
|
if cs.Width.Min < w {
|
|
cs.Width.Min = w
|
|
}
|
|
if w < cs.Width.Max {
|
|
cs.Width.Max = w
|
|
}
|
|
}
|
|
return cs
|
|
}
|
|
|
|
type Align struct {
|
|
Alignment Direction
|
|
cs Constraints
|
|
}
|
|
|
|
func (a *Align) Begin(ops *ui.Ops, cs Constraints) Constraints {
|
|
a.cs = cs
|
|
ops.Begin()
|
|
return cs.Loose()
|
|
}
|
|
|
|
func (a *Align) End(ops *ui.Ops, dims Dimens) Dimens {
|
|
block := ops.End()
|
|
sz := dims.Size
|
|
if a.cs.Width.Max != ui.Inf {
|
|
sz.X = a.cs.Width.Max
|
|
}
|
|
if a.cs.Height.Max != ui.Inf {
|
|
sz.Y = a.cs.Height.Max
|
|
}
|
|
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
|
|
}
|
|
ops.Begin()
|
|
ui.OpTransform{Transform: ui.Offset(toPointF(p))}.Add(ops)
|
|
block.Add(ops)
|
|
ops.End().Add(ops)
|
|
return Dimens{
|
|
Size: sz,
|
|
Baseline: dims.Baseline,
|
|
}
|
|
}
|