2023-07-02 00:52:14 -06:00
|
|
|
package x
|
|
|
|
|
2023-07-05 01:25:50 -06:00
|
|
|
import "image"
|
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
|
|
|
|
|
2023-07-06 00:08:10 -06:00
|
|
|
gap image.Point
|
2023-07-05 01:25:50 -06:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2023-07-06 00:08:10 -06:00
|
|
|
func (this *containerBox) SetGap (gap image.Point) {
|
2023-07-05 01:25:50 -06:00
|
|
|
if this.gap == gap { return }
|
|
|
|
this.gap = gap
|
|
|
|
this.invalidateLayout()
|
2023-07-11 23:17:12 -06:00
|
|
|
this.recalculateMinimumSize()
|
2023-07-05 01:25:50 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
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()
|
2023-07-11 23:17:12 -06:00
|
|
|
this.recalculateMinimumSize()
|
2023-07-05 01:25:50 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
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()
|
2023-07-11 23:17:12 -06:00
|
|
|
this.recalculateMinimumSize()
|
2023-07-05 01:25:50 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
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()
|
2023-07-11 23:17:12 -06:00
|
|
|
this.recalculateMinimumSize()
|
2023-07-05 01:25:50 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func (this *containerBox) Clear () {
|
|
|
|
for _, box := range this.children {
|
|
|
|
box.setParent(nil)
|
|
|
|
}
|
|
|
|
this.children = nil
|
|
|
|
this.invalidateLayout()
|
2023-07-11 23:17:12 -06:00
|
|
|
this.recalculateMinimumSize()
|
2023-07-05 01:25:50 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
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()
|
2023-07-11 23:17:12 -06:00
|
|
|
this.recalculateMinimumSize()
|
2023-07-05 01:25:50 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func (this *containerBox) Draw (can canvas.Canvas) {
|
2023-07-11 23:17:12 -06:00
|
|
|
if can == nil { return }
|
2023-07-05 01:25:50 -06:00
|
|
|
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()
|
|
|
|
}
|
2023-07-05 02:11:24 -06:00
|
|
|
for _, tile := range canvas.Shatter(this.bounds, rocks...) {
|
2023-07-05 01:25:50 -06:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2023-07-06 00:08:10 -06:00
|
|
|
func (this *containerBox) layoutHints () tomo.LayoutHints {
|
|
|
|
innerBounds := this.InnerBounds().Sub(this.scroll)
|
|
|
|
return tomo.LayoutHints {
|
|
|
|
Bounds: innerBounds,
|
|
|
|
OverflowX: this.hOverflow,
|
|
|
|
OverflowY: this.vOverflow,
|
|
|
|
Gap: this.gap,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-11 23:17:12 -06:00
|
|
|
func (this *containerBox) recalculateMinimumSize () {
|
|
|
|
// TODO calculate minimum size and use SetMinimumSize
|
|
|
|
}
|
|
|
|
|
2023-07-05 01:25:50 -06:00
|
|
|
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 {
|
2023-07-06 00:08:10 -06:00
|
|
|
this.layout.Arrange(this.layoutHints(), boxes)
|
2023-07-05 01:25:50 -06:00
|
|
|
}
|
|
|
|
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
|
|
|
}
|