package ui import ( "opslag.de/schobers/geom" ) type Spacing struct { Proxy 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 } func sumLengths(lengths ...float32) (float32, int) { var fixed float32 var infinite int for _, l := range lengths { if geom.IsNaN32(l) { infinite++ } else { fixed += l } } return fixed, infinite } func addMargin(bounds geom.PointF32, size geom.PointF32, margin SideLengths) geom.RectangleF32 { var left = margin.Left.Value() var top = margin.Top.Value() var w, hor = sumLengths(left, size.X, margin.Right.Value()) var h, ver = sumLengths(top, size.Y, margin.Bottom.Value()) var rem = geom.PtF32(geom.Max32(0, bounds.X-w), geom.Max32(0, bounds.Y-h)) if geom.IsNaN32(left) { left = rem.X / float32(hor) } else if rem.X == 0 { left = (left * bounds.X) / w } if geom.IsNaN32(size.X) { size.X = rem.X / float32(hor) } else if rem.X == 0 { size.X = (size.X * bounds.X) / w } else if hor == 0 { // nothing stretches size.X += rem.X } if geom.IsNaN32(top) { top = rem.Y / float32(ver) } else if rem.Y == 0 { top = (top * bounds.Y) / h } if geom.IsNaN32(size.Y) { size.Y = rem.Y / float32(ver) } else if rem.Y == 0 { size.Y = (size.Y * bounds.Y) / h } else if ver == 0 { // nothing stretches size.Y += rem.Y } return geom.RectF32(left, top, left+size.X, top+size.Y) } func (s *Spacing) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.PointF32) { var size = s.Proxy.DesiredSize(ctx) var w, h = s.Width.Zero(size.X), s.Height.Zero(size.Y) var content = addMargin(geom.PtF32(bounds.Dx(), bounds.Dy()), geom.PtF32(w, h), s.Margin).Add(bounds.Min) s.Proxy.Arrange(ctx, content, offset) } 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) var margin = func(l *Length) float32 { var v = l.Value() if geom.IsNaN32(v) { return 0 } return v } return geom.PtF32( margin(s.Margin.Left)+w+margin(s.Margin.Right), margin(s.Margin.Top)+h+margin(s.Margin.Bottom)) } func (s *Spacing) SetSize(w, h float32) { s.Width = Fixed(w) s.Height = Fixed(h) } // FixedSize wraps the Control c to fill exactly the space specified. func FixedSize(c Control, w, h float32) *Spacing { 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. func FixedHeight(c Control, h float32) *Spacing { 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. func Stretch(c Control) *Spacing { return stretch(c, true, true) } // StretchHeight wraps the Control c to stretch vertically. Width is taken from Control c. func StretchHeight(c Control) *Spacing { return stretch(c, false, true) } // StretchWidth wraps the Control c to stretch horizontally. Height is taken from Control c. func StretchWidth(c Control) *Spacing { return stretch(c, true, false) }