This repository has been archived on 2024-06-02. You can view files and clone it, but cannot push or open issues or pull requests.
x/containerbox.go

177 lines
4.1 KiB
Go
Raw Normal View History

2023-07-02 00:52:14 -06:00
package x
2023-07-05 01:25:50 -06:00
import "image"
import "image/color"
2023-07-02 00:52:14 -06:00
import "git.tebibyte.media/tomo/tomo"
2023-07-05 01:25:50 -06:00
import "git.tebibyte.media/tomo/tomo/event"
import "git.tebibyte.media/tomo/tomo/canvas"
type containerBox struct {
*box
hOverflow, vOverflow bool
contentBounds image.Rectangle
scroll image.Point
gap tomo.Gap
children []anyBox
layout tomo.Layout
on struct {
contentBoundsChange event.FuncBroadcaster
}
}
2023-07-02 00:52:14 -06:00
func (backend *Backend) NewContainerBox() tomo.ContainerBox {
2023-07-05 01:25:50 -06:00
box := &containerBox {
box: backend.NewBox().(*box),
}
box.drawer = box
box.outer = box
return box
}
func (this *containerBox) Box () tomo.Box {
return this
}
func (this *containerBox) SetOverflow (horizontal, vertical bool) {
if this.hOverflow == horizontal && this.vOverflow == vertical { return }
this.hOverflow = horizontal
this.vOverflow = vertical
this.invalidateLayout()
}
func (this *containerBox) ContentBounds () image.Rectangle {
return this.contentBounds
}
func (this *containerBox) ScrollTo (point image.Point) {
// TODO: constrain scroll
this.scroll = point
this.invalidateLayout()
}
func (this *containerBox) OnContentBoundsChange (callback func()) event.Cookie {
return this.on.contentBoundsChange.Connect(callback)
}
func (this *containerBox) SetGap (gap tomo.Gap) {
if this.gap == gap { return }
this.gap = gap
this.invalidateLayout()
}
func (this *containerBox) Add (child tomo.Object) {
box := assertAnyBox(child.Box())
if indexOf(this.children, box) > -1 { return }
box.setParent(this)
this.children = append(this.children, box)
this.invalidateLayout()
}
func (this *containerBox) Delete (child tomo.Object) {
box := assertAnyBox(child.Box())
index := indexOf(this.children, box)
if index < 0 { return }
box.setParent(nil)
this.children = remove(this.children, index)
this.invalidateLayout()
}
func (this *containerBox) Insert (child, before tomo.Object) {
box := assertAnyBox(child.Box())
if indexOf(this.children, box) > -1 { return }
beforeBox := assertAnyBox(before.Box())
index := indexOf(this.children, beforeBox)
if index < 0 { return }
box.setParent(this)
this.children = insert(this.children, index, box)
this.invalidateLayout()
}
func (this *containerBox) Clear () {
for _, box := range this.children {
box.setParent(nil)
}
this.children = nil
this.invalidateLayout()
}
func (this *containerBox) Length () int {
return len(this.children)
}
func (this *containerBox) At (index int) tomo.Object {
if index < 0 || index >= len(this.children) {
return nil
}
return this.children[index]
}
func (this *containerBox) SetLayout (layout tomo.Layout) {
this.layout = layout
this.invalidateLayout()
}
func (this *containerBox) Draw (can canvas.Canvas) {
this.drawBorders(can)
pen := can.Pen()
pen.Fill(this.color)
// TODO: do this in doLayout and save the result
rocks := make([]image.Rectangle, len(this.children))
for index, box := range this.children {
rocks[index] = box.Bounds()
}
// TODO: use shatter algorithm here to optimize amount of pixels drawn
// and not draw over child boxes
for index, tile := range canvas.Shatter(this.bounds, rocks...) {
pen.Fill(color.RGBA { R: uint8(32 * (index + 2)), A: 255 })
pen.Rectangle(tile)
}
}
func (this *containerBox) window () *window {
if this.parent == nil { return nil }
return this.parent.window()
}
func (this *containerBox) canvas () canvas.Canvas {
return this.box.canvas
}
func (this *containerBox) doLayout () {
this.box.doLayout()
// TODO: possibly store all children as tomo.Box-es and don't allocate a
// slice here
previousContentBounds := this.contentBounds
boxes := make([]tomo.Box, len(this.children))
for index, box := range this.children {
boxes[index] = box
}
if this.layout != nil {
// TODO maybe we should pass more information into Arrange such
// as overflow information and scroll
this.layout.Arrange (
this.InnerBounds().Sub(this.scroll),
this.gap,
boxes)
}
if previousContentBounds != this.contentBounds {
this.on.contentBoundsChange.Broadcast()
}
}
func (this *containerBox) recursiveRedo () {
this.doLayout()
this.doDraw()
for _, child := range this.children {
child.recursiveRedo()
}
2023-07-02 00:52:14 -06:00
}