diff --git a/layouts/cut.go b/layouts/cut.go new file mode 100644 index 0000000..cbc3bd4 --- /dev/null +++ b/layouts/cut.go @@ -0,0 +1,200 @@ +package layouts + +import "image" +import "git.tebibyte.media/tomo/tomo" + +type Cut struct { + branches [2]*Cut + expand [2]bool + vertical bool +} + +func (this *Cut) Vertical () (top, bottom *Cut) { + this.fill() + this.even() + this.vertical = true + return this.Branches() +} + +func (this *Cut) Top () (top, bottom *Cut) { + this.fill() + this.first() + this.vertical = true + return this.Branches() +} + +func (this *Cut) Bottom () (top, bottom *Cut) { + this.fill() + this.second() + this.vertical = true + return this.Branches() +} + +func (this *Cut) Horizontal () (top, bottom *Cut) { + this.fill() + this.even() + return this.Branches() +} + +func (this *Cut) Left () (top, bottom *Cut) { + this.fill() + this.first() + return this.Branches() +} + +func (this *Cut) Right () (top, bottom *Cut) { + this.fill() + this.second() + return this.Branches() +} + +func (this *Cut) Branches () (first, second *Cut) { + return this.branches[0], this.branches[1] +} + +func (this *Cut) real () bool { + return this != nil && this.branches[0] != nil && this.branches[1] != nil +} + +func (this *Cut) fill () { + this.branches[0] = &Cut { } + this.branches[1] = &Cut { } +} + +func (this *Cut) first () { + this.expand[0] = true + this.expand[1] = false +} + +func (this *Cut) second () { + this.expand[0] = false + this.expand[1] = true +} + +func (this *Cut) even () { + this.expand[0] = true + this.expand[1] = true +} + +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 { + // collect minimum sizes and physical endpoints + var minimums [2]image.Point + var leaves [2]tomo.Box + 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 [2]image.Rectangle + 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 +}