Compare commits
No commits in common. "1cb9e8091ed41a9376a5832df0f0e1439a7a3bd3" and "f025ec3d8aaf835dc6ba273db0802eb5cb02b4c3" have entirely different histories.
1cb9e8091e
...
f025ec3d8a
123
layouts/flow.go
123
layouts/flow.go
@ -1,123 +0,0 @@
|
|||||||
package layouts
|
|
||||||
|
|
||||||
import "image"
|
|
||||||
import "git.tebibyte.media/tomo/tomo"
|
|
||||||
|
|
||||||
// Flow is a grid layout where the number of rows and columns changes depending
|
|
||||||
// on the size of the container. It is designed to be used with an overflowing
|
|
||||||
// container. If the container does not overflow in the correct direction, the
|
|
||||||
// layout will behave like Contract.
|
|
||||||
type Flow bool
|
|
||||||
|
|
||||||
// FlowVertical is a vertical flow layout.
|
|
||||||
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 {
|
|
||||||
// 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) {
|
|
||||||
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())
|
|
||||||
if boxSize > minorSize { minorSize = boxSize }
|
|
||||||
}
|
|
||||||
if minorSize == 0 { return }
|
|
||||||
minorSteps :=
|
|
||||||
(flow.deltaMinor(hints.Bounds) + flow.minor(hints.Gap)) /
|
|
||||||
(minorSize + flow.minor(hints.Gap))
|
|
||||||
|
|
||||||
// arrange
|
|
||||||
point := hints.Bounds.Min
|
|
||||||
index := 0
|
|
||||||
for index < len(boxes) {
|
|
||||||
// get a slice of boxes for this major step
|
|
||||||
stepIndexEnd := index + minorSteps
|
|
||||||
if stepIndexEnd >= len(boxes) { stepIndexEnd = len(boxes) - 1 }
|
|
||||||
step := boxes[index:stepIndexEnd]
|
|
||||||
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())
|
|
||||||
if boxSize > majorSize { majorSize = boxSize }
|
|
||||||
}
|
|
||||||
if majorSize == 0 { continue }
|
|
||||||
|
|
||||||
// arrange all boxes on this major step
|
|
||||||
var size image.Point
|
|
||||||
size = flow.incrMajor(size, majorSize)
|
|
||||||
size = flow.incrMinor(size, minorSize)
|
|
||||||
for _, box := range step {
|
|
||||||
bounds := image.Rectangle { Min: point, Max: point.Add(size) }
|
|
||||||
box.SetBounds(bounds)
|
|
||||||
|
|
||||||
point = flow.incrMinor(point, minorSize + flow.minor(hints.Gap))
|
|
||||||
}
|
|
||||||
|
|
||||||
if flow.v() {
|
|
||||||
point.Y += majorSize + hints.Gap.Y
|
|
||||||
point.X = hints.Bounds.Min.X
|
|
||||||
} else {
|
|
||||||
point.X += majorSize + hints.Gap.X
|
|
||||||
point.Y = hints.Bounds.Min.Y
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (flow Flow) v () bool { return flow == FlowVertical }
|
|
||||||
func (flow Flow) h () bool { return flow == FlowHorizontal }
|
|
||||||
|
|
||||||
func (flow Flow) minor (point image.Point) int {
|
|
||||||
if flow.v() {
|
|
||||||
return point.X
|
|
||||||
} else {
|
|
||||||
return point.Y
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func (flow Flow) major (point image.Point) int {
|
|
||||||
if flow.v() {
|
|
||||||
return point.Y
|
|
||||||
} else {
|
|
||||||
return point.X
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func (flow Flow) incrMinor (point image.Point, delta int) image.Point {
|
|
||||||
if flow.v() {
|
|
||||||
point.X += delta
|
|
||||||
return point
|
|
||||||
} else {
|
|
||||||
point.Y += delta
|
|
||||||
return point
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func (flow Flow) incrMajor (point image.Point, delta int) image.Point {
|
|
||||||
if flow.v() {
|
|
||||||
point.Y += delta
|
|
||||||
return point
|
|
||||||
} else {
|
|
||||||
point.X += delta
|
|
||||||
return point
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func (flow Flow) deltaMinor (rectangle image.Rectangle) int {
|
|
||||||
if flow.v() {
|
|
||||||
return rectangle.Dx()
|
|
||||||
} else {
|
|
||||||
return rectangle.Dy()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func (flow Flow) fallback () tomo.Layout {
|
|
||||||
return Contract(flow)
|
|
||||||
}
|
|
@ -3,93 +3,86 @@ package layouts
|
|||||||
import "image"
|
import "image"
|
||||||
import "git.tebibyte.media/tomo/tomo"
|
import "git.tebibyte.media/tomo/tomo"
|
||||||
|
|
||||||
// Contract is a layout that arranges boxes in a simple row or column according
|
type Row struct { }
|
||||||
// to their minimum sizes.
|
|
||||||
type Contract bool
|
|
||||||
|
|
||||||
// ContractVertical is a vertical contracted layout.
|
func (Row) MinimumSize (hints tomo.LayoutHints, boxes []tomo.Box) image.Point {
|
||||||
const ContractVertical Contract = true
|
dot := image.Point { }
|
||||||
|
for _, box := range boxes {
|
||||||
// ContractHorizontal is a horizontal contracted layout.
|
minimum := box.MinimumSize()
|
||||||
const ContractHorizontal Contract = false
|
dot.X += minimum.X
|
||||||
|
if dot.Y < minimum.Y {
|
||||||
func (contract Contract) MinimumSize (hints tomo.LayoutHints, boxes []tomo.Box) image.Point {
|
dot.Y = minimum.Y
|
||||||
if contract.v() {
|
|
||||||
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
|
dot.X += hints.Gap.X * (len(boxes) - 1)
|
||||||
} else {
|
return dot
|
||||||
dot := image.Point { }
|
}
|
||||||
for _, box := range boxes {
|
|
||||||
minimum := box.MinimumSize()
|
func (Row) Arrange (hints tomo.LayoutHints, boxes []tomo.Box) {
|
||||||
dot.X += minimum.X
|
// TODO respect alignment value
|
||||||
if dot.Y < minimum.Y {
|
dot := hints.Bounds.Min
|
||||||
dot.Y = minimum.Y
|
for index, box := range boxes {
|
||||||
}
|
if index > 0 { dot.X += hints.Gap.X }
|
||||||
}
|
minimum := box.MinimumSize()
|
||||||
dot.X += hints.Gap.X * (len(boxes) - 1)
|
box.SetBounds(image.Rectangle {
|
||||||
return dot
|
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) Arrange (hints tomo.LayoutHints, boxes []tomo.Box) {
|
type Column struct { }
|
||||||
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
|
func (Column) MinimumSize (hints tomo.LayoutHints, boxes []tomo.Box) image.Point {
|
||||||
offset := 0
|
dot := image.Point { }
|
||||||
|
for _, box := range boxes {
|
||||||
switch hints.AlignX {
|
minimum := box.MinimumSize()
|
||||||
case tomo.AlignMiddle:
|
dot.Y += minimum.Y
|
||||||
offset = (hints.Bounds.Dx() - width) / 2
|
if dot.X < minimum.X {
|
||||||
case tomo.AlignEnd:
|
dot.X = minimum.X
|
||||||
offset = hints.Bounds.Dx() - width
|
|
||||||
}
|
|
||||||
for _, box := range boxes {
|
|
||||||
box.SetBounds(box.Bounds().Add(image.Pt(offset, 0)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
dot.Y += hints.Gap.Y * (len(boxes) - 1)
|
||||||
|
return dot
|
||||||
}
|
}
|
||||||
|
|
||||||
func (contract Contract) v () bool { return contract == ContractVertical }
|
func (Column) Arrange (hints tomo.LayoutHints, boxes []tomo.Box) {
|
||||||
func (contract Contract) h () bool { return contract == ContractHorizontal }
|
// TODO respect alignment value
|
||||||
|
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)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user