package ui import ( "opslag.de/schobers/geom" ) type StackPanel struct { ContainerBase Orientation Orientation } func BuildStackPanel(o Orientation, fn func(*StackPanel)) *StackPanel { var p = &StackPanel{Orientation: o} if fn != nil { fn(p) } return p } func (p *StackPanel) preArrange(ctx Context, bounds geom.RectangleF32, clip bool) []geom.RectangleF32 { bounds = p.Orientation.FlipRect(bounds) var length float32 var stretch int var desired = make([]geom.PointF32, len(p.Children)) for i, child := range p.Children { var size = p.Orientation.FlipPt(child.DesiredSize(ctx, p.Orientation.FlipPt(bounds.Size()))) if geom.IsNaN32(size.Y) { stretch++ } else { length += size.Y } desired[i] = size } var remainder = bounds.Dy() - length var childOffset float32 childBounds := make([]geom.RectangleF32, 0, len(p.Children)) for _, size := range desired { var height = size.Y if geom.IsNaN32(size.Y) { if remainder < 0 { height = 0 } else { height = remainder / float32(stretch) } } minY := bounds.Min.Y + childOffset maxY := minY + height if clip { if minY > bounds.Max.Y { minY = bounds.Max.Y } if maxY > bounds.Max.Y { maxY = bounds.Max.Y } } var child = geom.RectF32(bounds.Min.X, minY, bounds.Max.X, maxY) childBounds = append(childBounds, p.Orientation.FlipRect(child)) childOffset += height } return childBounds } func (p *StackPanel) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.PointF32, parent Control) { childBounds := p.preArrange(ctx, bounds, true) for i := range p.Children { p.Children[i].Arrange(ctx, p.Orientation.FlipRect(childBounds[i]), offset, p) } p.ControlBase.Arrange(ctx, p.Orientation.FlipRect(bounds), offset, parent) } func (p *StackPanel) BoundsUnclipped(ctx Context, path ControlPath) geom.RectangleF32 { if len(path) == 0 { return p.bounds } next := path[0] childBounds := p.preArrange(ctx, p.bounds, false) for i, child := range p.Children { if child != next && next.Self() != child.Self() { continue } return childBounds[i] } panic("child not found in path") } func (p *StackPanel) DesiredSize(ctx Context, size geom.PointF32) geom.PointF32 { var length float32 var width float32 for _, child := range p.Children { var size = child.DesiredSize(ctx, size) var l, w = p.Orientation.LengthParallel(size), p.Orientation.LengthPerpendicular(size) if geom.IsNaN32(l) { length = l } else { if !geom.IsNaN32(length) { length += l } } if geom.IsNaN32(w) { width = w } else { if !geom.IsNaN32(width) { width = geom.Max32(width, w) } } } return p.Orientation.Pt(length, width) } func (p *StackPanel) Render(ctx Context) { p.RenderBackground(ctx) var bounds = p.Bounds() for _, child := range p.Children { var childB = child.Bounds() if childB.Min.X >= bounds.Max.X || childB.Min.Y >= bounds.Max.Y || childB.Max.X < bounds.Min.X || childB.Max.Y < bounds.Min.Y || childB.Max.X < 0 || childB.Max.Y < 0 { continue } child.Render(ctx) } } func (p *StackPanel) ScrollIntoView(ctx Context, path ControlPath) { if p.parent == nil { return } p.parent.ScrollIntoView(ctx, path.Prepend(p)) }