Improvements to TextBox and ContainerBox

This commit is contained in:
Sasha Koshka 2023-07-12 18:36:11 -04:00
parent 069b8898f3
commit 34ca98f865
7 changed files with 77 additions and 33 deletions

15
box.go
View File

@ -70,6 +70,10 @@ func (this *box) InnerBounds () image.Rectangle {
return this.padding.Apply(this.innerClippingBounds())
}
func (this *box) MinimumSize () image.Point {
return this.minSize
}
func (this *box) innerClippingBounds () image.Rectangle {
innerBounds := this.bounds
for _, border := range this.border {
@ -95,13 +99,12 @@ func (this *box) SetBorder (border ...tomo.Border) {
this.invalidateLayout()
}
func (this *box) SetMinimumSize (width, height int) {
minSize := image.Pt(width, height)
if this.minSize == minSize { return }
this.minSize = minSize
func (this *box) SetMinimumSize (size image.Point) {
if this.minSize == size { return }
this.minSize = size
if this.bounds.Dx() < width || this.bounds.Dy() < height {
// TODO: alert the parent
if this.parent != nil {
this.parent.notifyMinimumSizeChange(this)
}
}

View File

@ -13,7 +13,7 @@ type containerBox struct {
scroll image.Point
gap image.Point
children []anyBox
children []tomo.Box
layout tomo.Layout
on struct {
@ -64,7 +64,7 @@ func (this *containerBox) SetGap (gap image.Point) {
func (this *containerBox) Add (child tomo.Object) {
box := assertAnyBox(child.Box())
if indexOf(this.children, box) > -1 { return }
if indexOf(this.children, tomo.Box(box)) > -1 { return }
box.setParent(this)
this.children = append(this.children, box)
@ -74,7 +74,7 @@ func (this *containerBox) Add (child tomo.Object) {
func (this *containerBox) Delete (child tomo.Object) {
box := assertAnyBox(child.Box())
index := indexOf(this.children, box)
index := indexOf(this.children, tomo.Box(box))
if index < 0 { return }
box.setParent(nil)
@ -85,21 +85,21 @@ func (this *containerBox) Delete (child tomo.Object) {
func (this *containerBox) Insert (child, before tomo.Object) {
box := assertAnyBox(child.Box())
if indexOf(this.children, box) > -1 { return }
if indexOf(this.children, tomo.Box(box)) > -1 { return }
beforeBox := assertAnyBox(before.Box())
index := indexOf(this.children, beforeBox)
index := indexOf(this.children, tomo.Box(beforeBox))
if index < 0 { return }
box.setParent(this)
this.children = insert(this.children, index, box)
this.children = insert(this.children, index, tomo.Box(box))
this.invalidateLayout()
this.recalculateMinimumSize()
}
func (this *containerBox) Clear () {
for _, box := range this.children {
box.setParent(nil)
box.(anyBox).setParent(nil)
}
this.children = nil
this.invalidateLayout()
@ -148,6 +148,16 @@ func (this *containerBox) canvas () canvas.Canvas {
return this.box.canvas
}
func (this *containerBox) notifyMinimumSizeChange (child anyBox) {
this.recalculateMinimumSize()
size := child.MinimumSize()
bounds := child.Bounds()
if bounds.Dx() < size.X || bounds.Dy() < size.Y {
this.invalidateLayout()
}
}
func (this *containerBox) layoutHints () tomo.LayoutHints {
innerBounds := this.InnerBounds().Sub(this.scroll)
return tomo.LayoutHints {
@ -159,20 +169,21 @@ func (this *containerBox) layoutHints () tomo.LayoutHints {
}
func (this *containerBox) recalculateMinimumSize () {
// TODO calculate minimum size and use SetMinimumSize
if this.layout == nil {
this.SetMinimumSize(image.Point { })
return
}
minimum := this.layout.MinimumSize(this.layoutHints(), this.children)
minimum.X += this.padding.Horizontal()
minimum.Y += this.padding.Vertical()
this.SetMinimumSize(minimum)
}
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 {
this.layout.Arrange(this.layoutHints(), boxes)
this.layout.Arrange(this.layoutHints(), this.children)
}
if previousContentBounds != this.contentBounds {
this.on.contentBoundsChange.Broadcast()
@ -183,6 +194,6 @@ func (this *containerBox) recursiveRedo () {
this.doLayout()
this.doDraw()
for _, child := range this.children {
child.recursiveRedo()
child.(anyBox).recursiveRedo()
}
}

2
go.mod
View File

@ -4,7 +4,7 @@ go 1.20
require (
git.tebibyte.media/tomo/ggfx v0.4.0
git.tebibyte.media/tomo/tomo v0.11.0
git.tebibyte.media/tomo/tomo v0.12.0
git.tebibyte.media/tomo/typeset v0.3.0
git.tebibyte.media/tomo/xgbkb v1.0.1
github.com/jezek/xgb v1.1.0

2
go.sum
View File

@ -3,6 +3,8 @@ git.tebibyte.media/tomo/ggfx v0.4.0 h1:3aUHeGS/yYWRV/zCDubBsXnik5ygkMnj/VgrM5Z75
git.tebibyte.media/tomo/ggfx v0.4.0/go.mod h1:zPoz8BdVQyG2KhEmeGFQBK66V71i6Kj8oVFbrZaCwRA=
git.tebibyte.media/tomo/tomo v0.11.0 h1:Gh9c/6rDqvhxt/DaNQHYNUfdRmSQTuz9T3F+pb5W6BI=
git.tebibyte.media/tomo/tomo v0.11.0/go.mod h1:lTwjpiHbP4UN/kFw+6FwhG600B+PMKVtMOr7wpd5IUY=
git.tebibyte.media/tomo/tomo v0.12.0 h1:MwcudNzo7hSeiEWGSLt6lxJGK3itxTSgdggaZoHZLJo=
git.tebibyte.media/tomo/tomo v0.12.0/go.mod h1:lTwjpiHbP4UN/kFw+6FwhG600B+PMKVtMOr7wpd5IUY=
git.tebibyte.media/tomo/typeset v0.1.0 h1:ZLwQzy51vUskjg1nB4Emjag8VXn3ki2jEkE19kwVQ4c=
git.tebibyte.media/tomo/typeset v0.1.0/go.mod h1:PwDpSdBF3l/EzoIsa2ME7QffVVajnTHZN6l3MHEGe1g=
git.tebibyte.media/tomo/typeset v0.2.0 h1:7DcnB0sW12eL+MxkEMv99eVG2IQxsZHDDK6pz6VE1O8=

View File

@ -32,8 +32,9 @@ func (set *boxSet) Pop () anyBox {
}
type parent interface {
window () *window
canvas () canvas.Canvas
window () *window
canvas () canvas.Canvas
notifyMinimumSizeChange (anyBox)
}
type anyBox interface {
@ -64,6 +65,7 @@ func (window *window) SetRoot (root tomo.Object) {
window.invalidateLayout(box)
window.root = box
}
window.recalculateMinimumSize()
}
func (window *window) window () *window {
@ -74,6 +76,10 @@ func (window *window) canvas () canvas.Canvas {
return window.xCanvas
}
func (window *window) notifyMinimumSizeChange (anyBox) {
window.recalculateMinimumSize()
}
func (window *window) invalidateDraw (box anyBox) {
window.needDraw.Add(box)
}

View File

@ -31,6 +31,8 @@ func (backend *Backend) NewTextBox() tomo.TextBox {
box := &textBox {
box: backend.NewBox().(*box),
textColor: color.Black,
hOverflow: false,
vOverflow: false,
}
box.box.drawer = box
box.outer = box
@ -119,6 +121,18 @@ func (this *textBox) recalculateMinimumSize () {
func (this *textBox) doLayout () {
this.box.doLayout()
previousContentBounds := this.contentBounds
innerBounds := this.InnerBounds()
if this.hOverflow {
this.drawer.SetMaxWidth(0)
} else {
this.drawer.SetMaxWidth(innerBounds.Dx())
}
if this.vOverflow {
this.drawer.SetMaxHeight(0)
} else {
this.drawer.SetMaxHeight(innerBounds.Dy())
}
this.contentBounds = this.drawer.LayoutBoundsSpace()
this.contentBounds = this.contentBounds.Sub(this.contentBounds.Min)

View File

@ -128,7 +128,7 @@ func (backend *Backend) newWindow (
// Connect(backend.x, window.xWindow.Id)
window.metrics.bounds = bounds
window.setMinimumSize(8, 8)
window.setMinimumSize(image.Pt(8, 8))
backend.windows[window.xWindow.Id] = window
@ -351,21 +351,29 @@ func (window *window) pushRegion (region image.Rectangle) {
subCanvas.(*xcanvas.Canvas).Push(window.xWindow.Id)
}
func (window *window) setMinimumSize (width, height int) {
if width < 8 { width = 8 }
if height < 8 { height = 8 }
func (window *window) recalculateMinimumSize () {
rootMinimum := image.Point { }
if window.root != nil {
rootMinimum = window.root.MinimumSize()
}
window.setMinimumSize(rootMinimum)
}
func (window *window) setMinimumSize (size image.Point) {
if size.X < 8 { size.X = 8 }
if size.Y < 8 { size.Y = 8 }
icccm.WmNormalHintsSet (
window.backend.x,
window.xWindow.Id,
&icccm.NormalHints {
Flags: icccm.SizeHintPMinSize,
MinWidth: uint(width),
MinHeight: uint(height),
MinWidth: uint(size.X),
MinHeight: uint(size.Y),
})
newWidth := window.metrics.bounds.Dx()
newHeight := window.metrics.bounds.Dy()
if newWidth < width { newWidth = width }
if newHeight < height { newHeight = height }
if newWidth < size.X { newWidth = size.X }
if newHeight < size.Y { newHeight = size.Y }
if newWidth != window.metrics.bounds.Dx() ||
newHeight != window.metrics.bounds.Dy() {
window.xWindow.Resize(newWidth, newHeight)