Compare commits

..

No commits in common. "1596d54834fed9020a4a068c7a67dbbe630d00aa" and "6a8aaca18de3bd83dd6a3c771413a843491c4abd" have entirely different histories.

8 changed files with 176 additions and 62 deletions

View File

@ -28,6 +28,7 @@ func newContainer (layout tomo.Layout, children ...tomo.Object) *Container {
func NewOuterContainer (layout tomo.Layout, children ...tomo.Object) *Container { func NewOuterContainer (layout tomo.Layout, children ...tomo.Object) *Container {
this := newContainer(layout, children...) this := newContainer(layout, children...)
this.SetRole(tomo.R("objects", "Container", "outer")) this.SetRole(tomo.R("objects", "Container", "outer"))
tomo.Apply(this)
return this return this
} }
@ -36,6 +37,7 @@ func NewOuterContainer (layout tomo.Layout, children ...tomo.Object) *Container
func NewSunkenContainer (layout tomo.Layout, children ...tomo.Object) *Container { func NewSunkenContainer (layout tomo.Layout, children ...tomo.Object) *Container {
this := newContainer(layout, children...) this := newContainer(layout, children...)
this.SetRole(tomo.R("objects", "Container", "sunken")) this.SetRole(tomo.R("objects", "Container", "sunken"))
tomo.Apply(this)
return this return this
} }
@ -43,6 +45,7 @@ func NewSunkenContainer (layout tomo.Layout, children ...tomo.Object) *Container
func NewInnerContainer (layout tomo.Layout, children ...tomo.Object) *Container { func NewInnerContainer (layout tomo.Layout, children ...tomo.Object) *Container {
this := newContainer(layout, children...) this := newContainer(layout, children...)
this.SetRole(tomo.R("objects", "Container", "inner")) this.SetRole(tomo.R("objects", "Container", "inner"))
tomo.Apply(this)
return this return this
} }

View File

@ -3,8 +3,6 @@ package layouts
import "image" import "image"
import "git.tebibyte.media/tomo/tomo" import "git.tebibyte.media/tomo/tomo"
var _ tomo.Layout = ContractVertical
// Contract is a layout that arranges boxes in a simple row or column according // Contract is a layout that arranges boxes in a simple row or column according
// to their minimum sizes. // to their minimum sizes.
type Contract bool type Contract bool
@ -42,7 +40,6 @@ func (contract Contract) MinimumSize (hints tomo.LayoutHints, boxes []tomo.Box)
} }
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
if contract.v() { if contract.v() {
dot := hints.Bounds.Min dot := hints.Bounds.Min
for index, box := range boxes { for index, box := range boxes {
@ -94,15 +91,5 @@ func (contract Contract) Arrange (hints tomo.LayoutHints, boxes []tomo.Box) {
} }
} }
func (contract Contract) RecommendedHeight (hints tomo.LayoutHints, boxes []tomo.Box, width int) int {
// TODO
return 0
}
func (contract Contract) RecommendedWidth (hints tomo.LayoutHints, boxes []tomo.Box, height int) int {
// TODO
return 0
}
func (contract Contract) v () bool { return contract == ContractVertical } func (contract Contract) v () bool { return contract == ContractVertical }
func (contract Contract) h () bool { return contract == ContractHorizontal } func (contract Contract) h () bool { return contract == ContractHorizontal }

173
layouts/cut.go Normal file
View File

@ -0,0 +1,173 @@
package layouts
import "image"
import "git.tebibyte.media/tomo/tomo"
// Cut is a layout that can be divided into smaller and smaller sections.
type Cut struct {
branches []*Cut
expand []bool
vertical bool
}
// NewCut creates and returns a new Cut layout.
func NewCut () *Cut {
return new(Cut)
}
// Vertical divides the layout vertically. Sections are specified using
// booleans. If a section is true, it will expand. If false, it will contract.
func (this *Cut) Vertical (expand ...bool) {
this.expand = expand
this.vertical = true
this.fill()
}
// Horizontal divides the layout horizontally. Sections are specified using
// booleans. If a section is true, it will expand. If false, it will contract.
func (this *Cut) Horizontal (expand ...bool) {
this.expand = expand
this.vertical = false
this.fill()
}
// At returns the section of this layout at the specified index.
func (this *Cut) At (index int) *Cut {
return this.branches[index]
}
func (this *Cut) real () bool {
return this != nil && this.branches != nil
}
func (this *Cut) fill () {
this.branches = make([]*Cut, len(this.expand))
for index := range this.branches {
this.branches[index] = new(Cut)
}
}
func (this *Cut) MinimumSize (hints tomo.LayoutHints, boxes []tomo.Box) image.Point {
size, _ := this.minimumSize(hints, boxes)
return size
}
func (this *Cut) Arrange (hints tomo.LayoutHints, boxes []tomo.Box) {
this.arrange(hints, boxes)
}
func (this *Cut) minimumSize (hints tomo.LayoutHints, boxes []tomo.Box) (image.Point, []tomo.Box) {
size := image.Point { }
for index, branch := range this.branches {
if len(boxes) == 0 { break }
var point image.Point
if branch.real() {
point, boxes = branch.minimumSize(hints, boxes)
} else {
point = boxes[0].MinimumSize()
boxes = boxes[1:]
}
if this.vertical {
if point.X > size.X { size.X = point.X }
if index > 0 { size.Y += hints.Gap.Y }
size.Y += point.Y
} else {
if point.Y > size.Y { size.Y = point.Y }
if index > 0 { size.X += hints.Gap.X }
size.X += point.X
}
}
return size, boxes
}
func (this *Cut) arrange (hints tomo.LayoutHints, boxes []tomo.Box) []tomo.Box {
nChildren := len(this.branches)
// collect minimum sizes and physical endpoints
var minimums = make([]image.Point, nChildren)
var leaves = make([]tomo.Box, nChildren)
var nBranches int
remaining := boxes
for index, branch := range this.branches {
if branch.real() {
minimums[index], remaining = branch.minimumSize(hints, remaining)
} else {
if len(remaining) == 0 { break }
leaves[index] = remaining[0]
minimums[index] = remaining[0].MinimumSize()
remaining = remaining[1:]
}
nBranches ++
}
// determine the amount of space to divide among expanding branches
gaps := nBranches - 1
var freeSpace float64; if this.vertical {
freeSpace = float64(hints.Bounds.Dy() - hints.Gap.Y * gaps)
} else {
freeSpace = float64(hints.Bounds.Dx() - hints.Gap.X * gaps)
}
var nExpanding float64
for index, minimum := range minimums {
if this.expand[index] {
nExpanding ++
} else if this.vertical {
freeSpace -= float64(minimum.Y)
} else {
freeSpace -= float64(minimum.X)
}
}
expandingSize := freeSpace / nExpanding
// calculate the size and position of branches
var bounds = make([]image.Rectangle, nChildren)
x := float64(hints.Bounds.Min.X)
y := float64(hints.Bounds.Min.Y)
for index, minimum := range minimums {
// get size along significant axis
var size float64; if this.expand[index] {
size = expandingSize
} else if this.vertical {
size = float64(minimum.Y)
} else {
size = float64(minimum.X)
}
// figure out bounds from size
if this.vertical {
bounds[index].Max = image.Pt (
int(hints.Bounds.Dx()),
int(size))
} else {
bounds[index].Max = image.Pt (
int(size),
int(hints.Bounds.Dy()))
}
bounds[index] = bounds[index].Add(image.Pt(int(x), int(y)))
// move along
if this.vertical {
y += float64(hints.Gap.Y) + size
} else {
x += float64(hints.Gap.X) + size
}
}
// apply the size and position
for index, bound := range bounds {
if leaves[index] != nil {
leaves[index].SetBounds(bound)
boxes = boxes[1:]
} else if this.branches[index] != nil {
newHints := hints
newHints.Bounds = bound
boxes = this.branches[index].arrange(newHints, boxes)
}
}
return boxes
}

View File

@ -3,8 +3,6 @@ package layouts
import "image" import "image"
import "git.tebibyte.media/tomo/tomo" import "git.tebibyte.media/tomo/tomo"
var _ tomo.Layout = FlowVertical
// Flow is a grid layout where the number of rows and columns changes depending // 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 // 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 // container. If the container does not overflow in the correct direction, the
@ -123,13 +121,3 @@ func (flow Flow) deltaMinor (rectangle image.Rectangle) int {
func (flow Flow) fallback () tomo.Layout { func (flow Flow) fallback () tomo.Layout {
return Contract(flow) return Contract(flow)
} }
func (flow Flow) RecommendedHeight (hints tomo.LayoutHints, boxes []tomo.Box, width int) int {
// TODO
return 0
}
func (flow Flow) RecommendedWidth (hints tomo.LayoutHints, boxes []tomo.Box, height int) int {
// TODO
return 0
}

View File

@ -4,8 +4,6 @@ import "math"
import "image" import "image"
import "git.tebibyte.media/tomo/tomo" import "git.tebibyte.media/tomo/tomo"
var _ tomo.Layout = new(Grid)
// Grid is a layout that arranges boxes in a grid formation with distinct rows // Grid is a layout that arranges boxes in a grid formation with distinct rows
// and columns. It is great for creating forms. // and columns. It is great for creating forms.
type Grid struct { type Grid struct {
@ -108,13 +106,3 @@ func expand (hints tomo.LayoutHints, sizes []int, space int, expands func (int)
func ceilDiv (x, y int) int { func ceilDiv (x, y int) int {
return int(math.Ceil(float64(x) / float64(y))) return int(math.Ceil(float64(x) / float64(y)))
} }
func (this *Grid) RecommendedHeight (hints tomo.LayoutHints, boxes []tomo.Box, width int) int {
// TODO
return 0
}
func (this *Grid) RecommendedWidth (hints tomo.LayoutHints, boxes []tomo.Box, height int) int {
// TODO
return 0
}

View File

@ -335,14 +335,6 @@ func (this scrollbarLayout) Arrange (hints tomo.LayoutHints, boxes []tomo.Box) {
} }
func (this scrollbarLayout) RecommendedHeight (tomo.LayoutHints, []tomo.Box, int) int {
return 0
}
func (this scrollbarLayout) RecommendedWidth (tomo.LayoutHints, []tomo.Box, int) int {
return 0
}
func (this scrollbarLayout) viewportContentRatio () float64 { func (this scrollbarLayout) viewportContentRatio () float64 {
if this.linked == nil { return 0 } if this.linked == nil { return 0 }
return this.viewportLength() / this.contentLength() return this.viewportLength() / this.contentLength()

View File

@ -133,7 +133,6 @@ func (this *ScrollContainer) handleScroll (x, y float64) {
Sub(image.Pt(int(x), int(y)))) Sub(image.Pt(int(x), int(y))))
} }
// TODO: remove this and replace it with something from the layouts package
type scrollContainerLayout struct { type scrollContainerLayout struct {
root tomo.ContentObject root tomo.ContentObject
horizontal *Scrollbar horizontal *Scrollbar
@ -184,14 +183,6 @@ func (this *scrollContainerLayout) Arrange (hints tomo.LayoutHints, boxes []tomo
} }
} }
func (this *scrollContainerLayout) RecommendedHeight (tomo.LayoutHints, []tomo.Box, int) int {
return 0
}
func (this *scrollContainerLayout) RecommendedWidth (tomo.LayoutHints, []tomo.Box, int) int {
return 0
}
func max (x, y int) int { func max (x, y int) int {
if x > y { return x } if x > y { return x }
return y return y

View File

@ -238,11 +238,3 @@ func (this sliderLayout) Arrange (hints tomo.LayoutHints, boxes []tomo.Box) {
Add(gutter.Min)) Add(gutter.Min))
} }
} }
func (this sliderLayout) RecommendedHeight (tomo.LayoutHints, []tomo.Box, int) int {
return 0
}
func (this sliderLayout) RecommendedWidth (tomo.LayoutHints, []tomo.Box, int) int {
return 0
}