Improvements to TextBox and ContainerBox
This commit is contained in:
parent
069b8898f3
commit
34ca98f865
15
box.go
15
box.go
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
2
go.mod
@ -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
2
go.sum
@ -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=
|
||||||
|
10
system.go
10
system.go
@ -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)
|
||||||
}
|
}
|
||||||
|
14
textbox.go
14
textbox.go
@ -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)
|
||||||
|
24
window.go
24
window.go
@ -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)
|
||||||
|
Reference in New Issue
Block a user