diff --git a/box.go b/box.go index 7277f1b..e163827 100644 --- a/box.go +++ b/box.go @@ -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) } } diff --git a/containerbox.go b/containerbox.go index 8a737dc..c581c70 100644 --- a/containerbox.go +++ b/containerbox.go @@ -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() } } diff --git a/go.mod b/go.mod index 80553ad..6d53af8 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index f719bd6..dc19c62 100644 --- a/go.sum +++ b/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/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= diff --git a/system.go b/system.go index 83cf00b..79a0bc3 100644 --- a/system.go +++ b/system.go @@ -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) } diff --git a/textbox.go b/textbox.go index c6dbfab..3b6a20e 100644 --- a/textbox.go +++ b/textbox.go @@ -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) diff --git a/window.go b/window.go index 56fad2b..82295c7 100644 --- a/window.go +++ b/window.go @@ -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)