Contract layouts are now based on new Row and Column layouts

This commit is contained in:
Sasha Koshka 2024-06-12 03:15:38 -04:00
parent 1596d54834
commit 9ce7f8b8f3
3 changed files with 214 additions and 82 deletions

View File

@ -5,9 +5,9 @@ import "git.tebibyte.media/tomo/tomo/input"
import "git.tebibyte.media/tomo/tomo/event" import "git.tebibyte.media/tomo/tomo/event"
import "git.tebibyte.media/tomo/objects/layouts" import "git.tebibyte.media/tomo/objects/layouts"
var buttonLayout = layouts.NewGrid([]bool { true }, []bool { true }) var buttonLayout = layouts.Row { true }
var iconButtonLayout = layouts.NewGrid([]bool { true }, []bool { true }) var iconButtonLayout = layouts.Row { true }
var bothButtonLayout = layouts.NewGrid([]bool { false, true }, []bool { true }) var bothButtonLayout = layouts.Row { false, true }
// Button is a clickable button. // Button is a clickable button.
type Button struct { type Button struct {

View File

@ -16,93 +16,25 @@ const ContractVertical Contract = true
const ContractHorizontal Contract = false 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.Box) image.Point {
if contract.v() { return contract.fallback().MinimumSize(hints, boxes)
dot := image.Point { }
for _, box := range boxes {
minimum := box.MinimumSize()
dot.Y += minimum.Y
if dot.X < minimum.X {
dot.X = minimum.X
}
}
dot.Y += hints.Gap.Y * (len(boxes) - 1)
return dot
} else {
dot := image.Point { }
for _, box := range boxes {
minimum := box.MinimumSize()
dot.X += minimum.X
if dot.Y < minimum.Y {
dot.Y = minimum.Y
}
}
dot.X += hints.Gap.X * (len(boxes) - 1)
return dot
}
} }
func (contract Contract) Arrange (hints tomo.LayoutHints, boxes []tomo.Box) { func (contract Contract) Arrange (hints tomo.LayoutHints, boxes []tomo.Box) {
// TODO if we overflow in a direction, respect the reccomended size contract.fallback().Arrange(hints, boxes)
if contract.v() {
dot := hints.Bounds.Min
for index, box := range boxes {
if index > 0 { dot.Y += hints.Gap.Y }
minimum := box.MinimumSize()
box.SetBounds(image.Rectangle {
Min: dot,
Max: dot.Add(image.Pt(hints.Bounds.Dx(), minimum.Y)),
})
dot.Y += minimum.Y
}
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)))
}
} else {
dot := hints.Bounds.Min
for index, box := range boxes {
if index > 0 { dot.X += hints.Gap.X }
minimum := box.MinimumSize()
box.SetBounds(image.Rectangle {
Min: dot,
Max: dot.Add(image.Pt(minimum.X, hints.Bounds.Dy())),
})
dot.X += minimum.X
}
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)))
}
}
} }
func (contract Contract) RecommendedHeight (hints tomo.LayoutHints, boxes []tomo.Box, width int) int { func (contract Contract) RecommendedHeight (hints tomo.LayoutHints, boxes []tomo.Box, width int) int {
// TODO return contract.fallback().RecommendedHeight(hints, boxes, width)
return 0
} }
func (contract Contract) RecommendedWidth (hints tomo.LayoutHints, boxes []tomo.Box, height int) int { func (contract Contract) RecommendedWidth (hints tomo.LayoutHints, boxes []tomo.Box, height int) int {
// TODO return contract.fallback().RecommendedWidth(hints, boxes, height)
return 0
} }
func (contract Contract) v () bool { return contract == ContractVertical } func (contract Contract) fallback () tomo.Layout {
func (contract Contract) h () bool { return contract == ContractHorizontal } if contract == ContractVertical {
return Column { }
} else {
return Row { }
}
}

200
layouts/rowcol.go Normal file
View File

@ -0,0 +1,200 @@
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.Box) image.Point {
dot := image.Point { }
for _, box := range boxes {
minimum := box.MinimumSize()
dot.Y += minimum.Y
if dot.X < minimum.X {
dot.X = minimum.X
}
}
dot.Y += hints.Gap.Y * (len(boxes) - 1)
return dot
}
func (row Row) MinimumSize (hints tomo.LayoutHints, boxes []tomo.Box) image.Point {
dot := image.Point { }
for _, box := range boxes {
minimum := box.MinimumSize()
dot.X += minimum.X
if dot.Y < minimum.Y {
dot.Y = minimum.Y
}
}
dot.X += hints.Gap.X * (len(boxes) - 1)
return dot
}
func (column Column) Arrange (hints tomo.LayoutHints, boxes []tomo.Box) {
expands := func (index int) bool {
if index < len(boxes) { return false }
return column[index]
}
// determine expanding box size
expandingSize := 0
if !hints.OverflowY {
gaps := len(boxes) - 1
freeSpace := float64(hints.Bounds.Dy() - hints.Gap.Y * gaps)
nExpanding := 0; for index, box := range boxes {
if expands(index) {
nExpanding ++
} else {
freeSpace -= float64(box.MinimumSize().X)
}
}
}
// determine width
width := 0
if hints.OverflowX {
for _, box := range boxes {
minimum := box.MinimumSize()
if width < minimum.X { width = minimum.X }
}
} else {
width = hints.Bounds.Dx()
}
// arrange
dot := hints.Bounds.Min
for index, box := range boxes {
if index > 0 { dot.Y += hints.Gap.Y }
// determine height
height := box.MinimumSize().Y
if hints.OverflowY {
if box, ok := box.(tomo.ContentBox); ok {
height = box.RecommendedHeight(width)
}
} else {
if expands(index) {
height = expandingSize
}
}
// set bounds
box.SetBounds(image.Rectangle {
Min: dot,
Max: dot.Add(image.Pt(width, height)),
})
dot.Y += height
}
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)))
}
}
func (row Row) Arrange (hints tomo.LayoutHints, boxes []tomo.Box) {
expands := func (index int) bool {
if index < len(boxes) { return false }
return row[index]
}
// determine expanding box size
expandingSize := 0
if !hints.OverflowY {
gaps := len(boxes) - 1
freeSpace := float64(hints.Bounds.Dx() - hints.Gap.X * gaps)
nExpanding := 0; for index, box := range boxes {
if expands(index) {
nExpanding ++
} else {
freeSpace -= float64(box.MinimumSize().Y)
}
}
}
// determine height
height := 0
if hints.OverflowY {
for _, box := range boxes {
minimum := box.MinimumSize()
if height < minimum.Y { height = minimum.Y }
}
} else {
height = hints.Bounds.Dy()
}
// arrange
dot := hints.Bounds.Min
for index, box := range boxes {
if index > 0 { dot.X += hints.Gap.X }
// determine width
width := box.MinimumSize().X
if hints.OverflowY {
if box, ok := box.(tomo.ContentBox); ok {
width = box.RecommendedHeight(height)
}
} else {
if expands(index) {
width = expandingSize
}
}
// set bounds
box.SetBounds(image.Rectangle {
Min: dot,
Max: dot.Add(image.Pt(width, height)),
})
dot.X += width
}
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)))
}
}
func (column Column) RecommendedHeight (hints tomo.LayoutHints, boxes []tomo.Box, width int) int {
// TODO
return 0
}
func (row Row) RecommendedHeight (hints tomo.LayoutHints, boxes []tomo.Box, width int) int {
// TODO
return 0
}
func (column Column) RecommendedWidth (hints tomo.LayoutHints, boxes []tomo.Box, height int) int {
// TODO
return 0
}
func (row Row) RecommendedWidth (hints tomo.LayoutHints, boxes []tomo.Box, height int) int {
// TODO
return 0
}