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

View File

@ -13,7 +13,7 @@ type containerBox struct {
scroll image.Point scroll image.Point
gap image.Point gap image.Point
children []anyBox children []tomo.Box
layout tomo.Layout layout tomo.Layout
on struct { on struct {
@ -64,7 +64,7 @@ func (this *containerBox) SetGap (gap image.Point) {
func (this *containerBox) Add (child tomo.Object) { func (this *containerBox) Add (child tomo.Object) {
box := assertAnyBox(child.Box()) box := assertAnyBox(child.Box())
if indexOf(this.children, box) > -1 { return } if indexOf(this.children, tomo.Box(box)) > -1 { return }
box.setParent(this) box.setParent(this)
this.children = append(this.children, box) this.children = append(this.children, box)
@ -74,7 +74,7 @@ func (this *containerBox) Add (child tomo.Object) {
func (this *containerBox) Delete (child tomo.Object) { func (this *containerBox) Delete (child tomo.Object) {
box := assertAnyBox(child.Box()) box := assertAnyBox(child.Box())
index := indexOf(this.children, box) index := indexOf(this.children, tomo.Box(box))
if index < 0 { return } if index < 0 { return }
box.setParent(nil) box.setParent(nil)
@ -85,21 +85,21 @@ func (this *containerBox) Delete (child tomo.Object) {
func (this *containerBox) Insert (child, before tomo.Object) { func (this *containerBox) Insert (child, before tomo.Object) {
box := assertAnyBox(child.Box()) box := assertAnyBox(child.Box())
if indexOf(this.children, box) > -1 { return } if indexOf(this.children, tomo.Box(box)) > -1 { return }
beforeBox := assertAnyBox(before.Box()) beforeBox := assertAnyBox(before.Box())
index := indexOf(this.children, beforeBox) index := indexOf(this.children, tomo.Box(beforeBox))
if index < 0 { return } if index < 0 { return }
box.setParent(this) box.setParent(this)
this.children = insert(this.children, index, box) this.children = insert(this.children, index, tomo.Box(box))
this.invalidateLayout() this.invalidateLayout()
this.recalculateMinimumSize() this.recalculateMinimumSize()
} }
func (this *containerBox) Clear () { func (this *containerBox) Clear () {
for _, box := range this.children { for _, box := range this.children {
box.setParent(nil) box.(anyBox).setParent(nil)
} }
this.children = nil this.children = nil
this.invalidateLayout() this.invalidateLayout()
@ -148,6 +148,16 @@ func (this *containerBox) canvas () canvas.Canvas {
return this.box.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 { func (this *containerBox) layoutHints () tomo.LayoutHints {
innerBounds := this.InnerBounds().Sub(this.scroll) innerBounds := this.InnerBounds().Sub(this.scroll)
return tomo.LayoutHints { return tomo.LayoutHints {
@ -159,20 +169,21 @@ func (this *containerBox) layoutHints () tomo.LayoutHints {
} }
func (this *containerBox) recalculateMinimumSize () { 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 () { func (this *containerBox) doLayout () {
this.box.doLayout() this.box.doLayout()
// TODO: possibly store all children as tomo.Box-es and don't allocate a
// slice here
previousContentBounds := this.contentBounds previousContentBounds := this.contentBounds
boxes := make([]tomo.Box, len(this.children))
for index, box := range this.children {
boxes[index] = box
}
if this.layout != nil { if this.layout != nil {
this.layout.Arrange(this.layoutHints(), boxes) this.layout.Arrange(this.layoutHints(), this.children)
} }
if previousContentBounds != this.contentBounds { if previousContentBounds != this.contentBounds {
this.on.contentBoundsChange.Broadcast() this.on.contentBoundsChange.Broadcast()
@ -183,6 +194,6 @@ func (this *containerBox) recursiveRedo () {
this.doLayout() this.doLayout()
this.doDraw() this.doDraw()
for _, child := range this.children { for _, child := range this.children {
child.recursiveRedo() child.(anyBox).recursiveRedo()
} }
} }

2
go.mod
View File

@ -4,7 +4,7 @@ go 1.20
require ( require (
git.tebibyte.media/tomo/ggfx v0.4.0 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/typeset v0.3.0
git.tebibyte.media/tomo/xgbkb v1.0.1 git.tebibyte.media/tomo/xgbkb v1.0.1
github.com/jezek/xgb v1.1.0 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/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 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.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 h1:ZLwQzy51vUskjg1nB4Emjag8VXn3ki2jEkE19kwVQ4c=
git.tebibyte.media/tomo/typeset v0.1.0/go.mod h1:PwDpSdBF3l/EzoIsa2ME7QffVVajnTHZN6l3MHEGe1g= git.tebibyte.media/tomo/typeset v0.1.0/go.mod h1:PwDpSdBF3l/EzoIsa2ME7QffVVajnTHZN6l3MHEGe1g=
git.tebibyte.media/tomo/typeset v0.2.0 h1:7DcnB0sW12eL+MxkEMv99eVG2IQxsZHDDK6pz6VE1O8= 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 { type parent interface {
window () *window window () *window
canvas () canvas.Canvas canvas () canvas.Canvas
notifyMinimumSizeChange (anyBox)
} }
type anyBox interface { type anyBox interface {
@ -64,6 +65,7 @@ func (window *window) SetRoot (root tomo.Object) {
window.invalidateLayout(box) window.invalidateLayout(box)
window.root = box window.root = box
} }
window.recalculateMinimumSize()
} }
func (window *window) window () *window { func (window *window) window () *window {
@ -74,6 +76,10 @@ func (window *window) canvas () canvas.Canvas {
return window.xCanvas return window.xCanvas
} }
func (window *window) notifyMinimumSizeChange (anyBox) {
window.recalculateMinimumSize()
}
func (window *window) invalidateDraw (box anyBox) { func (window *window) invalidateDraw (box anyBox) {
window.needDraw.Add(box) window.needDraw.Add(box)
} }

View File

@ -31,6 +31,8 @@ func (backend *Backend) NewTextBox() tomo.TextBox {
box := &textBox { box := &textBox {
box: backend.NewBox().(*box), box: backend.NewBox().(*box),
textColor: color.Black, textColor: color.Black,
hOverflow: false,
vOverflow: false,
} }
box.box.drawer = box box.box.drawer = box
box.outer = box box.outer = box
@ -119,6 +121,18 @@ func (this *textBox) recalculateMinimumSize () {
func (this *textBox) doLayout () { func (this *textBox) doLayout () {
this.box.doLayout() this.box.doLayout()
previousContentBounds := this.contentBounds 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.drawer.LayoutBoundsSpace()
this.contentBounds = this.contentBounds.Sub(this.contentBounds.Min) this.contentBounds = this.contentBounds.Sub(this.contentBounds.Min)

View File

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