2019-03-12 20:11:43 +00:00
|
|
|
package ui
|
|
|
|
|
|
|
|
import (
|
|
|
|
"opslag.de/schobers/geom"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Spacing struct {
|
|
|
|
Proxy
|
|
|
|
|
2019-04-11 21:38:32 +00:00
|
|
|
bounds geom.RectangleF32
|
|
|
|
|
2019-03-12 20:11:43 +00:00
|
|
|
Margin SideLengths
|
|
|
|
Width *Length
|
|
|
|
Height *Length
|
|
|
|
}
|
|
|
|
|
|
|
|
func BuildSpacing(content Control, fn func(*Spacing)) *Spacing {
|
|
|
|
s := &Spacing{}
|
|
|
|
s.Content = content
|
|
|
|
if fn != nil {
|
|
|
|
fn(s)
|
|
|
|
}
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
2019-04-11 21:38:32 +00:00
|
|
|
func insetMargins(bounds geom.RectangleF32, margin SideLengths, width, height float32) geom.RectangleF32 {
|
|
|
|
var zero = func(f float32) float32 {
|
|
|
|
if geom.IsNaN32(f) {
|
|
|
|
return 0
|
2019-03-12 20:11:43 +00:00
|
|
|
}
|
2019-04-11 21:38:32 +00:00
|
|
|
return f
|
2019-03-12 20:11:43 +00:00
|
|
|
}
|
2019-04-11 21:38:32 +00:00
|
|
|
var nan = func(f float32) int {
|
|
|
|
if geom.IsNaN32(f) {
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
return 0
|
2019-03-12 20:11:43 +00:00
|
|
|
}
|
2019-04-11 21:38:32 +00:00
|
|
|
var partial = func(f, p float32) float32 {
|
|
|
|
if geom.IsNaN32(f) {
|
|
|
|
return p
|
|
|
|
}
|
|
|
|
return f
|
2019-03-12 20:11:43 +00:00
|
|
|
}
|
2019-04-11 21:38:32 +00:00
|
|
|
var inset = func(min, max, marginMin, marginMax, length float32) (float32, float32) {
|
|
|
|
var available = max - min
|
|
|
|
var margins = zero(marginMin) + zero(marginMax)
|
|
|
|
if margins > available {
|
|
|
|
return min + zero(marginMin)*available/margins, max - zero(marginMax)*available/margins
|
|
|
|
}
|
|
|
|
var fixed = margins + zero(length)
|
|
|
|
if fixed > available {
|
|
|
|
return min + zero(marginMin), max - zero(marginMax)
|
|
|
|
}
|
|
|
|
var nans = nan(marginMin) + nan(length) + nan(marginMax)
|
|
|
|
var part float32 = 0
|
|
|
|
if nans > 0 {
|
|
|
|
part = (available - fixed) / float32(nans)
|
|
|
|
}
|
|
|
|
return min + partial(marginMin, part), max - partial(marginMax, part)
|
2019-03-12 20:11:43 +00:00
|
|
|
}
|
2019-04-11 21:38:32 +00:00
|
|
|
bounds.Min.X, bounds.Max.X = inset(bounds.Min.X, bounds.Max.X, margin.Left.Value(), margin.Right.Value(), width)
|
|
|
|
bounds.Min.Y, bounds.Max.Y = inset(bounds.Min.Y, bounds.Max.Y, margin.Top.Value(), margin.Bottom.Value(), height)
|
|
|
|
return bounds
|
2019-03-12 20:11:43 +00:00
|
|
|
}
|
|
|
|
|
2019-04-11 21:38:32 +00:00
|
|
|
func (s *Spacing) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.PointF32, parent Control) {
|
|
|
|
size := s.DesiredSize(ctx)
|
|
|
|
content := insetMargins(bounds, s.Margin, s.Width.Zero(size.X), s.Height.Zero(size.Y))
|
|
|
|
s.bounds = bounds
|
|
|
|
s.Proxy.Arrange(ctx, content, offset, parent)
|
2019-03-12 20:11:43 +00:00
|
|
|
}
|
|
|
|
|
2019-04-11 21:38:32 +00:00
|
|
|
func (s *Spacing) Bounds() geom.RectangleF32 { return s.bounds }
|
|
|
|
|
2019-03-12 20:11:43 +00:00
|
|
|
func (s *Spacing) Center() {
|
|
|
|
s.Margin.Left = Infinite()
|
|
|
|
s.Margin.Top = Infinite()
|
|
|
|
s.Margin.Right = Infinite()
|
|
|
|
s.Margin.Bottom = Infinite()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Spacing) DesiredSize(ctx Context) geom.PointF32 {
|
|
|
|
var size = s.Proxy.DesiredSize(ctx)
|
|
|
|
var w, h = s.Width.Zero(size.X), s.Height.Zero(size.Y)
|
2019-04-11 18:05:30 +00:00
|
|
|
var margin = func(l *Length) float32 {
|
|
|
|
var v = l.Value()
|
|
|
|
if geom.IsNaN32(v) {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
return v
|
|
|
|
}
|
2019-03-12 20:11:43 +00:00
|
|
|
return geom.PtF32(
|
2019-04-11 18:05:30 +00:00
|
|
|
margin(s.Margin.Left)+w+margin(s.Margin.Right),
|
|
|
|
margin(s.Margin.Top)+h+margin(s.Margin.Bottom))
|
2019-03-12 20:11:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Spacing) SetSize(w, h float32) {
|
|
|
|
s.Width = Fixed(w)
|
|
|
|
s.Height = Fixed(h)
|
|
|
|
}
|
2019-03-13 18:03:06 +00:00
|
|
|
|
|
|
|
// FixedSize wraps the Control c to fill exactly the space specified.
|
2019-04-10 19:26:57 +00:00
|
|
|
func FixedSize(c Control, w, h float32) *Spacing {
|
2019-03-13 18:03:06 +00:00
|
|
|
return BuildSpacing(c, func(s *Spacing) {
|
|
|
|
s.Width = Fixed(w)
|
|
|
|
s.Height = Fixed(h)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// FixedHeight wraps the Control c to fill exactly the height specified. Width is taken from Control c.
|
2019-04-10 19:26:57 +00:00
|
|
|
func FixedHeight(c Control, h float32) *Spacing {
|
2019-03-13 18:03:06 +00:00
|
|
|
return BuildSpacing(c, func(s *Spacing) {
|
|
|
|
s.Height = Fixed(h)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// FixedWidth wraps the Control c to fill exactly the width specified. Height is taken from Control c.
|
|
|
|
func FixedWidth(c Control, w float32) Control {
|
|
|
|
return BuildSpacing(c, func(s *Spacing) {
|
|
|
|
s.Width = Fixed(w)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Margin wraps the Control c to have equal margins on all sides.
|
|
|
|
func Margin(c Control, m float32) *Spacing {
|
|
|
|
return Margins(c, m, m, m, m)
|
|
|
|
}
|
|
|
|
|
|
|
|
// MarginAxes wraps the Control c to have same margins horizontally (left, right) and vertically (top, left).
|
|
|
|
func MarginAxes(c Control, hor, ver float32) *Spacing {
|
|
|
|
return Margins(c, hor, ver, hor, ver)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Margins wraps the Control c to have different margins on all sides.
|
|
|
|
func Margins(c Control, left, top, right, bottom float32) *Spacing {
|
|
|
|
return BuildSpacing(c, func(s *Spacing) {
|
|
|
|
s.Margin.Left = Fixed(left)
|
|
|
|
s.Margin.Top = Fixed(top)
|
|
|
|
s.Margin.Right = Fixed(right)
|
|
|
|
s.Margin.Bottom = Fixed(bottom)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func stretch(c Control, w, h bool) *Spacing {
|
|
|
|
return BuildSpacing(c, func(s *Spacing) {
|
|
|
|
if w {
|
|
|
|
s.Width = Infinite()
|
|
|
|
}
|
|
|
|
if h {
|
|
|
|
s.Height = Infinite()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Stretch wraps the Control c to stretch in both directions.
|
2019-04-10 19:26:57 +00:00
|
|
|
func Stretch(c Control) *Spacing { return stretch(c, true, true) }
|
2019-03-13 18:03:06 +00:00
|
|
|
|
|
|
|
// StretchHeight wraps the Control c to stretch vertically. Width is taken from Control c.
|
2019-04-10 19:26:57 +00:00
|
|
|
func StretchHeight(c Control) *Spacing { return stretch(c, false, true) }
|
2019-03-13 18:03:06 +00:00
|
|
|
|
|
|
|
// StretchWidth wraps the Control c to stretch horizontally. Height is taken from Control c.
|
2019-04-10 19:26:57 +00:00
|
|
|
func StretchWidth(c Control) *Spacing { return stretch(c, true, false) }
|