package layouts import "image" import "git.tebibyte.media/tomo/tomo" var _ tomo.Layout = ContractVertical // Row arranges boxes in a row. Boxes that share an index with a true value will // expand, and others will contract. type Row []bool // Column arranges boxes in a column. Boxes that share an index with a true // value will expand, and others will contract. type Column []bool func (column Column) MinimumSize (hints tomo.LayoutHints, boxes tomo.BoxQuerier) image.Point { dot := image.Point { } for index := 0; index < boxes.Len(); index ++ { minimum := boxes.MinimumSize(index) dot.Y += minimum.Y if dot.X < minimum.X { dot.X = minimum.X } } dot.Y += hints.Gap.Y * (boxes.Len() - 1) return dot } func (row Row) MinimumSize (hints tomo.LayoutHints, boxes tomo.BoxQuerier) image.Point { dot := image.Point { } for index := 0; index < boxes.Len(); index ++ { minimum := boxes.MinimumSize(index) dot.X += minimum.X if dot.Y < minimum.Y { dot.Y = minimum.Y } } dot.X += hints.Gap.X * (boxes.Len() - 1) return dot } func (column Column) Arrange (hints tomo.LayoutHints, boxes tomo.BoxArranger) { expands := func (index int) bool { if index >= len(column) { return false } return column[index] } // determine expanding box size expandingSize := 0.0 if !hints.OverflowY { gaps := boxes.Len() - 1 freeSpace := float64(hints.Bounds.Dy() - hints.Gap.Y * gaps) nExpanding := 0 for index := 0; index < boxes.Len(); index ++ { if expands(index) { nExpanding ++ } else { freeSpace -= float64(boxes.MinimumSize(index).Y) } } expandingSize = freeSpace / float64(nExpanding) } // determine width width := 0 if hints.OverflowX { for index := 0; index < boxes.Len(); index ++ { minimum := boxes.MinimumSize(index) if width < minimum.X { width = minimum.X } } } else { width = hints.Bounds.Dx() } // arrange dot := hints.Bounds.Min bounds := make([]image.Rectangle, boxes.Len()) for index := 0; index < boxes.Len(); index ++ { if index > 0 { dot.Y += hints.Gap.Y } // determine height height := boxes.MinimumSize(index).Y if hints.OverflowY { height = boxes.RecommendedHeight(index, width) } else { if expands(index) { height = int(expandingSize) } } // store bounds of this box bounds[index] = image.Rectangle { Min: dot, Max: dot.Add(image.Pt(width, height)), } dot.Y += height } // align height := dot.Y - hints.Bounds.Min.Y offset := 0 switch hints.AlignY { case tomo.AlignMiddle: offset = (hints.Bounds.Dy() - height) / 2 case tomo.AlignEnd: offset = hints.Bounds.Dy() - height } for index := 0; index < boxes.Len(); index ++ { boxes.SetBounds(index, bounds[index].Add(image.Pt(0, offset))) } } func (row Row) Arrange (hints tomo.LayoutHints, boxes tomo.BoxArranger) { expands := func (index int) bool { if index >= len(row) { return false } return row[index] } // determine expanding box size expandingSize := 0.0 if !hints.OverflowY { gaps := boxes.Len() - 1 freeSpace := float64(hints.Bounds.Dx() - hints.Gap.X * gaps) nExpanding := 0 for index := 0; index < boxes.Len(); index ++ { if expands(index) { nExpanding ++ } else { freeSpace -= float64(boxes.MinimumSize(index).X) } } expandingSize = freeSpace / float64(nExpanding) } // determine height height := 0 if hints.OverflowY { for index := 0; index < boxes.Len(); index ++ { minimum := boxes.MinimumSize(index) if height < minimum.Y { height = minimum.Y } } } else { height = hints.Bounds.Dy() } // arrange dot := hints.Bounds.Min bounds := make([]image.Rectangle, boxes.Len()) for index := 0; index < boxes.Len(); index ++ { if index > 0 { dot.X += hints.Gap.X } // determine width width := boxes.MinimumSize(index).X if hints.OverflowY { width = boxes.RecommendedHeight(index, height) } else { if expands(index) { width = int(expandingSize) } } // store bounds bounds[index] = image.Rectangle { Min: dot, Max: dot.Add(image.Pt(width, height)), } dot.X += width } // align width := dot.X - hints.Bounds.Min.X offset := 0 switch hints.AlignX { case tomo.AlignMiddle: offset = (hints.Bounds.Dx() - width) / 2 case tomo.AlignEnd: offset = hints.Bounds.Dx() - width } for index := 0; index < boxes.Len(); index ++ { boxes.SetBounds(index, bounds[index].Add(image.Pt(offset, 0))) } } func (column Column) RecommendedHeight (hints tomo.LayoutHints, boxes tomo.BoxQuerier, width int) int { height := 0 for index := 0; index < boxes.Len(); index ++ { height += boxes.RecommendedHeight(index, width) } height += hints.Gap.Y * (boxes.Len() - 1) return height } func (row Row) RecommendedHeight (hints tomo.LayoutHints, boxes tomo.BoxQuerier, width int) int { height := 0 for index := 0; index < boxes.Len(); index ++ { minimum := boxes.MinimumSize(index) boxHeight := boxes.RecommendedHeight(index, minimum.X) if boxHeight > height { height = boxHeight } } return height } func (column Column) RecommendedWidth (hints tomo.LayoutHints, boxes tomo.BoxQuerier, height int) int { width := 0 for index := 0; index < boxes.Len(); index ++ { minimum := boxes.MinimumSize(index) boxWidth := boxes.RecommendedWidth(index, minimum.Y) if boxWidth > width { width = boxWidth } } return width } func (row Row) RecommendedWidth (hints tomo.LayoutHints, boxes tomo.BoxQuerier, height int) int { width := 0 for index := 0; index < boxes.Len(); index ++ { width += boxes.RecommendedWidth(index, height) } width += hints.Gap.X * (boxes.Len() - 1) return width }