Update code for layouts, objects

This commit is contained in:
2024-07-21 11:48:28 -04:00
parent 9077015db6
commit 6ca6771fc6
26 changed files with 447 additions and 389 deletions

View File

@@ -15,19 +15,19 @@ const ContractVertical Contract = true
// ContractHorizontal is a horizontal contracted layout.
const ContractHorizontal Contract = false
func (contract Contract) MinimumSize (hints tomo.LayoutHints, boxes []tomo.Box) image.Point {
func (contract Contract) MinimumSize (hints tomo.LayoutHints, boxes tomo.BoxQuerier) image.Point {
return contract.fallback().MinimumSize(hints, boxes)
}
func (contract Contract) Arrange (hints tomo.LayoutHints, boxes []tomo.Box) {
func (contract Contract) Arrange (hints tomo.LayoutHints, boxes tomo.BoxArranger) {
contract.fallback().Arrange(hints, boxes)
}
func (contract Contract) RecommendedHeight (hints tomo.LayoutHints, boxes []tomo.Box, width int) int {
func (contract Contract) RecommendedHeight (hints tomo.LayoutHints, boxes tomo.BoxQuerier, width int) int {
return contract.fallback().RecommendedHeight(hints, boxes, width)
}
func (contract Contract) RecommendedWidth (hints tomo.LayoutHints, boxes []tomo.Box, height int) int {
func (contract Contract) RecommendedWidth (hints tomo.LayoutHints, boxes tomo.BoxQuerier, height int) int {
return contract.fallback().RecommendedWidth(hints, boxes, height)
}

View File

@@ -17,21 +17,21 @@ const FlowVertical Flow = true
// FlowHorizontal is a horizontal flow layout.
const FlowHorizontal Flow = false
func (flow Flow) MinimumSize (hints tomo.LayoutHints, boxes []tomo.Box) image.Point {
func (flow Flow) MinimumSize (hints tomo.LayoutHints, boxes tomo.BoxQuerier) image.Point {
// TODO: write down somewhere that layout minimums aren't taken into
// account when the respective direction is overflowed
return flow.fallback().MinimumSize(hints, boxes)
}
func (flow Flow) Arrange (hints tomo.LayoutHints, boxes []tomo.Box) {
func (flow Flow) Arrange (hints tomo.LayoutHints, boxes tomo.BoxArranger) {
if flow.v() && !hints.OverflowY || flow.h() && !hints.OverflowX {
flow.fallback().Arrange(hints, boxes)
}
// find a minor size value that will fit all boxes
minorSize := 0
for _, box := range boxes {
boxSize := flow.minor(box.MinimumSize())
for index := 0; index < boxes.Len(); index ++ {
boxSize := flow.minor(boxes.MinimumSize(index))
if boxSize > minorSize { minorSize = boxSize }
}
if minorSize == 0 { return }
@@ -43,17 +43,16 @@ func (flow Flow) Arrange (hints tomo.LayoutHints, boxes []tomo.Box) {
// arrange
point := hints.Bounds.Min
index := 0
for index < len(boxes) {
for index < boxes.Len() {
// get a slice of boxes for this major step
stepIndexEnd := index + minorSteps
if stepIndexEnd > len(boxes) { stepIndexEnd = len(boxes) }
step := boxes[index:stepIndexEnd]
if stepIndexEnd > boxes.Len() { stepIndexEnd = boxes.Len() }
index += minorSteps
// find a major size that will fit all boxes on this major step
majorSize := 0
for _, box := range step {
boxSize := flow.major(box.MinimumSize())
for index := index; index < stepIndexEnd; index ++ {
boxSize := flow.major(boxes.MinimumSize(index))
if boxSize > majorSize { majorSize = boxSize }
}
if majorSize == 0 { continue }
@@ -62,9 +61,9 @@ func (flow Flow) Arrange (hints tomo.LayoutHints, boxes []tomo.Box) {
var size image.Point
size = flow.incrMajor(size, majorSize)
size = flow.incrMinor(size, minorSize)
for _, box := range step {
for index := index; index < stepIndexEnd; index ++ {
bounds := image.Rectangle { Min: point, Max: point.Add(size) }
box.SetBounds(bounds)
boxes.SetBounds(index, bounds)
point = flow.incrMinor(point, minorSize + flow.minor(hints.Gap))
}
@@ -125,12 +124,12 @@ func (flow Flow) fallback () tomo.Layout {
return Contract(flow)
}
func (flow Flow) RecommendedHeight (hints tomo.LayoutHints, boxes []tomo.Box, width int) int {
func (flow Flow) RecommendedHeight (hints tomo.LayoutHints, boxes tomo.BoxQuerier, width int) int {
// TODO
return 0
}
func (flow Flow) RecommendedWidth (hints tomo.LayoutHints, boxes []tomo.Box, height int) int {
func (flow Flow) RecommendedWidth (hints tomo.LayoutHints, boxes tomo.BoxQuerier, height int) int {
// TODO
return 0
}

View File

@@ -32,7 +32,7 @@ func NewGrid (columns ...bool) func (rows ...bool) *Grid {
}
}
func (this *Grid) MinimumSize (hints tomo.LayoutHints, boxes []tomo.Box) image.Point {
func (this *Grid) MinimumSize (hints tomo.LayoutHints, boxes tomo.BoxQuerier) image.Point {
cols, rows := this.minimums(boxes)
size := image.Pt (
(len(cols) - 1) * hints.Gap.X,
@@ -42,7 +42,7 @@ func (this *Grid) MinimumSize (hints tomo.LayoutHints, boxes []tomo.Box) image.P
return size
}
func (this *Grid) Arrange (hints tomo.LayoutHints, boxes []tomo.Box) {
func (this *Grid) Arrange (hints tomo.LayoutHints, boxes tomo.BoxArranger) {
xExpand := func (index int) bool {
return this.xExpand[index]
}
@@ -56,9 +56,9 @@ func (this *Grid) Arrange (hints tomo.LayoutHints, boxes []tomo.Box) {
expand(hints, rows, hints.Bounds.Dy(), yExpand)
position := hints.Bounds.Min
for index, box := range boxes {
for index := 0; index < boxes.Len(); index ++ {
col, row := index % len(cols), index / len(cols)
box.SetBounds(image.Rectangle {
boxes.SetBounds(index, image.Rectangle {
Min: position,
Max: position.Add(image.Pt(cols[col], rows[row])),
})
@@ -71,13 +71,13 @@ func (this *Grid) Arrange (hints tomo.LayoutHints, boxes []tomo.Box) {
}
}
func (this *Grid) minimums (boxes []tomo.Box) ([]int, []int) {
func (this *Grid) minimums (boxes tomo.BoxQuerier) ([]int, []int) {
nCols, nRows := this.dimensions(boxes)
cols, rows := make([]int, nCols), make([]int, nRows)
for index, box := range boxes {
for index := 0; index < boxes.Len(); index ++ {
col, row := index % len(cols), index / len(cols)
minimum := box.MinimumSize()
minimum := boxes.MinimumSize(index)
if cols[col] < minimum.X {
cols[col] = minimum.X
}
@@ -89,8 +89,8 @@ func (this *Grid) minimums (boxes []tomo.Box) ([]int, []int) {
return cols, rows
}
func (this *Grid) dimensions (boxes []tomo.Box) (int, int) {
return len(this.xExpand), ceilDiv(len(boxes), len(this.xExpand))
func (this *Grid) dimensions (boxes tomo.BoxQuerier) (int, int) {
return len(this.xExpand), ceilDiv(boxes.Len(), len(this.xExpand))
}
func expand (hints tomo.LayoutHints, sizes []int, space int, expands func (int) bool) {
@@ -116,10 +116,10 @@ func ceilDiv (x, y int) int {
return int(math.Ceil(float64(x) / float64(y)))
}
func (this *Grid) RecommendedHeight (hints tomo.LayoutHints, boxes []tomo.Box, width int) int {
func (this *Grid) RecommendedHeight (hints tomo.LayoutHints, boxes tomo.BoxQuerier, width int) int {
return this.MinimumSize(hints, boxes).Y
}
func (this *Grid) RecommendedWidth (hints tomo.LayoutHints, boxes []tomo.Box, height int) int {
func (this *Grid) RecommendedWidth (hints tomo.LayoutHints, boxes tomo.BoxQuerier, height int) int {
return this.MinimumSize(hints, boxes).X
}

View File

@@ -13,33 +13,33 @@ type Row []bool
// value will expand, and others will contract.
type Column []bool
func (column Column) MinimumSize (hints tomo.LayoutHints, boxes []tomo.Box) image.Point {
func (column Column) MinimumSize (hints tomo.LayoutHints, boxes tomo.BoxQuerier) image.Point {
dot := image.Point { }
for _, box := range boxes {
minimum := box.MinimumSize()
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 * (len(boxes) - 1)
dot.Y += hints.Gap.Y * (boxes.Len() - 1)
return dot
}
func (row Row) MinimumSize (hints tomo.LayoutHints, boxes []tomo.Box) image.Point {
func (row Row) MinimumSize (hints tomo.LayoutHints, boxes tomo.BoxQuerier) image.Point {
dot := image.Point { }
for _, box := range boxes {
minimum := box.MinimumSize()
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 * (len(boxes) - 1)
dot.X += hints.Gap.X * (boxes.Len() - 1)
return dot
}
func (column Column) Arrange (hints tomo.LayoutHints, boxes []tomo.Box) {
func (column Column) Arrange (hints tomo.LayoutHints, boxes tomo.BoxArranger) {
expands := func (index int) bool {
if index >= len(column) { return false }
return column[index]
@@ -48,13 +48,14 @@ func (column Column) Arrange (hints tomo.LayoutHints, boxes []tomo.Box) {
// determine expanding box size
expandingSize := 0.0
if !hints.OverflowY {
gaps := len(boxes) - 1
gaps := boxes.Len() - 1
freeSpace := float64(hints.Bounds.Dy() - hints.Gap.Y * gaps)
nExpanding := 0; for index, box := range boxes {
nExpanding := 0;
for index := 0; index < boxes.Len(); index ++ {
if expands(index) {
nExpanding ++
} else {
freeSpace -= float64(box.MinimumSize().Y)
freeSpace -= float64(boxes.MinimumSize(index).Y)
}
}
expandingSize = freeSpace / float64(nExpanding)
@@ -63,8 +64,8 @@ func (column Column) Arrange (hints tomo.LayoutHints, boxes []tomo.Box) {
// determine width
width := 0
if hints.OverflowX {
for _, box := range boxes {
minimum := box.MinimumSize()
for index := 0; index < boxes.Len(); index ++ {
minimum := boxes.MinimumSize(index)
if width < minimum.X { width = minimum.X }
}
} else {
@@ -73,44 +74,43 @@ func (column Column) Arrange (hints tomo.LayoutHints, boxes []tomo.Box) {
// arrange
dot := hints.Bounds.Min
for index, box := range boxes {
bounds := make([]image.Rectangle, boxes.Len())
for index := 0; index < boxes.Len(); index ++ {
if index > 0 { dot.Y += hints.Gap.Y }
// determine height
height := box.MinimumSize().Y
height := boxes.MinimumSize(index).Y
if hints.OverflowY {
if box, ok := box.(tomo.ContentBox); ok {
height = box.RecommendedHeight(width)
}
height = boxes.RecommendedHeight(index, width)
} else {
if expands(index) {
height = int(expandingSize)
}
}
// set bounds
box.SetBounds(image.Rectangle {
// 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 _, box := range boxes {
box.SetBounds(box.Bounds().Add(image.Pt(0, offset)))
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.Box) {
func (row Row) Arrange (hints tomo.LayoutHints, boxes tomo.BoxArranger) {
expands := func (index int) bool {
if index >= len(row) { return false }
return row[index]
@@ -119,13 +119,14 @@ func (row Row) Arrange (hints tomo.LayoutHints, boxes []tomo.Box) {
// determine expanding box size
expandingSize := 0.0
if !hints.OverflowY {
gaps := len(boxes) - 1
gaps := boxes.Len() - 1
freeSpace := float64(hints.Bounds.Dx() - hints.Gap.X * gaps)
nExpanding := 0; for index, box := range boxes {
nExpanding := 0;
for index := 0; index < boxes.Len(); index ++ {
if expands(index) {
nExpanding ++
} else {
freeSpace -= float64(box.MinimumSize().X)
freeSpace -= float64(boxes.MinimumSize(index).X)
}
}
expandingSize = freeSpace / float64(nExpanding)
@@ -134,8 +135,8 @@ func (row Row) Arrange (hints tomo.LayoutHints, boxes []tomo.Box) {
// determine height
height := 0
if hints.OverflowY {
for _, box := range boxes {
minimum := box.MinimumSize()
for index := 0; index < boxes.Len(); index ++ {
minimum := boxes.MinimumSize(index)
if height < minimum.Y { height = minimum.Y }
}
} else {
@@ -144,95 +145,76 @@ func (row Row) Arrange (hints tomo.LayoutHints, boxes []tomo.Box) {
// arrange
dot := hints.Bounds.Min
for index, box := range boxes {
bounds := make([]image.Rectangle, boxes.Len())
for index := 0; index < boxes.Len(); index ++ {
if index > 0 { dot.X += hints.Gap.X }
// determine width
width := box.MinimumSize().X
width := boxes.MinimumSize(index).X
if hints.OverflowY {
if box, ok := box.(tomo.ContentBox); ok {
width = box.RecommendedHeight(height)
}
width = boxes.RecommendedHeight(index, height)
} else {
if expands(index) {
width = int(expandingSize)
}
}
// set bounds
box.SetBounds(image.Rectangle {
// 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 _, box := range boxes {
box.SetBounds(box.Bounds().Add(image.Pt(offset, 0)))
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.Box, width int) int {
func (column Column) RecommendedHeight (hints tomo.LayoutHints, boxes tomo.BoxQuerier, width int) int {
height := 0
for _, box := range boxes {
if box, ok := box.(tomo.ContentBox); ok {
height += box.RecommendedHeight(width)
} else {
height += box.MinimumSize().Y
}
for index := 0; index < boxes.Len(); index ++ {
height += boxes.RecommendedHeight(index, width)
}
height += hints.Gap.Y * (len(boxes) - 1)
height += hints.Gap.Y * (boxes.Len() - 1)
return height
}
func (row Row) RecommendedHeight (hints tomo.LayoutHints, boxes []tomo.Box, width int) int {
func (row Row) RecommendedHeight (hints tomo.LayoutHints, boxes tomo.BoxQuerier, width int) int {
height := 0
for _, box := range boxes {
minimum := box.MinimumSize()
boxHeight := 0
if box, ok := box.(tomo.ContentBox); ok {
boxHeight = box.RecommendedHeight(minimum.X)
} else {
boxHeight = minimum.Y
}
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.Box, height int) int {
func (column Column) RecommendedWidth (hints tomo.LayoutHints, boxes tomo.BoxQuerier, height int) int {
width := 0
for _, box := range boxes {
minimum := box.MinimumSize()
boxWidth := 0
if box, ok := box.(tomo.ContentBox); ok {
boxWidth = box.RecommendedHeight(minimum.Y)
} else {
boxWidth = minimum.X
}
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.Box, height int) int {
func (row Row) RecommendedWidth (hints tomo.LayoutHints, boxes tomo.BoxQuerier, height int) int {
width := 0
for _, box := range boxes {
if box, ok := box.(tomo.ContentBox); ok {
width += box.RecommendedWidth(height)
} else {
width += box.MinimumSize().X
}
for index := 0; index < boxes.Len(); index ++ {
width += boxes.RecommendedWidth(index, height)
}
width += hints.Gap.X * (len(boxes) - 1)
width += hints.Gap.X * (boxes.Len() - 1)
return width
}