diff --git a/artist/text.go b/artist/text.go index 2953c03..1cbd460 100644 --- a/artist/text.go +++ b/artist/text.go @@ -14,10 +14,11 @@ type characterLayout struct { } type wordLayout struct { - position image.Point - width int - spaceAfter int - text []characterLayout + position image.Point + width int + spaceAfter int + breaksAfter int + text []characterLayout } // Align specifies a text alignment method. @@ -167,13 +168,16 @@ func (drawer *TextDrawer) LineHeight () (height fixed.Int26_6) { func (drawer *TextDrawer) ReccomendedHeightFor (width int) (height int) { if !drawer.layoutClean { drawer.recalculate() } metrics := drawer.face.Metrics() - dot := fixed.Point26_6 { 0, 0 } + dot := fixed.Point26_6 { 0, metrics.Height } for _, word := range drawer.layout { - dot.X += fixed.Int26_6((word.width + word.spaceAfter) << 6) - - if word.width + word.position.X > width && word.position.X > 0 { + if word.width + dot.X.Round() > width { dot.Y += metrics.Height - dot.X = fixed.Int26_6(word.width << 6) + dot.X = 0 + } + dot.X += fixed.I(word.width + word.spaceAfter) + if word.breaksAfter > 0 { + dot.Y += fixed.I(word.breaksAfter).Mul(metrics.Height) + dot.X = 0 } } @@ -246,6 +250,7 @@ func (drawer *TextDrawer) recalculate () { if character == '\n' { dot.Y += metrics.Height dot.X = 0 + word.breaksAfter ++ previousCharacter = character index ++ } else { @@ -290,7 +295,6 @@ func (drawer *TextDrawer) recalculate () { if drawer.wrap { drawer.layoutBounds.Max.X = drawer.width - println("aaa") } else { drawer.layoutBounds.Max.X = horizontalExtent } diff --git a/backends/x/window.go b/backends/x/window.go index 56110b6..18dea52 100644 --- a/backends/x/window.go +++ b/backends/x/window.go @@ -89,6 +89,7 @@ func (window *Window) Adopt (child tomo.Element) { child.SetParentHooks (tomo.ParentHooks { Draw: window.childDrawCallback, MinimumSizeChange: window.childMinimumSizeChangeCallback, + ExpandingHeightChange: window.resizeChildToFit, SelectionRequest: window.childSelectionRequestCallback, }) @@ -205,8 +206,25 @@ func (window *Window) resizeChildToFit () { if child, ok := window.child.(tomo.Expanding); ok { minimumHeight := child.MinimumHeightFor(window.metrics.width) _, minimumWidth := child.MinimumSize() - window.childMinimumSizeChangeCallback ( - minimumWidth, minimumHeight) + + + icccm.WmNormalHintsSet ( + window.backend.connection, + window.xWindow.Id, + &icccm.NormalHints { + Flags: icccm.SizeHintPMinSize, + MinWidth: uint(minimumWidth), + MinHeight: uint(minimumHeight), + }) + + if window.metrics.height >= minimumHeight && + window.metrics.width >= minimumWidth { + + window.child.Resize ( + window.metrics.width, + window.metrics.height) + window.redrawChildEntirely() + } } else { window.child.Resize ( window.metrics.width, diff --git a/element.go b/element.go index bf6be5a..2bb31d9 100644 --- a/element.go +++ b/element.go @@ -15,6 +15,10 @@ type ParentHooks struct { // have already been resized and there is no need to send it a resize // event. MinimumSizeChange func (width, height int) + + // ExpandingHeightChange is called when the parameters affecting the + // element's expanding height have changed. + ExpandingHeightChange func () // SelectionRequest is called when the child element element wants // itself to be selected. If the parent element chooses to grant the @@ -42,6 +46,14 @@ func (hooks ParentHooks) RunMinimumSizeChange (width, height int) { } } +// RunExpandingHeightChange runs the ExpandingHeightChange hook if it is not +// nil. If it is nil, it does nothing. +func (hooks ParentHooks) RunExpandingHeightChange () { + if hooks.ExpandingHeightChange != nil { + hooks.ExpandingHeightChange() + } +} + // RunSelectionRequest runs the SelectionRequest hook if it is not nil. If it is // nil, it does nothing. func (hooks ParentHooks) RunSelectionRequest () (granted bool) { diff --git a/elements/basic/label.go b/elements/basic/label.go index 1b33373..fe0d584 100644 --- a/elements/basic/label.go +++ b/elements/basic/label.go @@ -27,6 +27,7 @@ func NewLabel (text string, wrap bool) (element *Label) { return } +// Resize resizes the label and re-wraps the text if wrapping is enabled. func (element *Label) Resize (width, height int) { element.core.AllocateCanvas(width, height) if element.wrap { @@ -37,6 +38,17 @@ func (element *Label) Resize (width, height int) { return } +// MinimumHeightFor returns the reccomended height for this element based on the +// given width in order to allow the text to wrap properly. +func (element *Label) MinimumHeightFor (width int) (height int) { + if element.wrap { + return element.drawer.ReccomendedHeightFor(width) + } else { + _, height = element.MinimumSize() + return + } +} + // SetText sets the label's text. func (element *Label) SetText (text string) { if element.text == text { return } @@ -76,6 +88,7 @@ func (element *Label) updateMinimumSize () { if em < 1 { em = theme.Padding() } element.core.SetMinimumSize ( em, element.drawer.LineHeight().Round()) + element.core.NotifyExpandingHeightChange() } else { bounds := element.drawer.LayoutBounds() element.core.SetMinimumSize(bounds.Dx(), bounds.Dy()) diff --git a/elements/core/core.go b/elements/core/core.go index e88cacb..1a5b455 100644 --- a/elements/core/core.go +++ b/elements/core/core.go @@ -144,6 +144,12 @@ func (control CoreControl) SetMinimumSize (width, height int) { } } +// NotifyExpandingHeightChange notifies the parent element that this element's +// expanding height has changed. +func (control CoreControl) NotifyExpandingHeightChange () { + control.core.hooks.RunExpandingHeightChange() +} + // ConstrainSize contstrains the specified width and height to the minimum width // and height, and returns wether or not anything ended up being constrained. func (control CoreControl) ConstrainSize ( diff --git a/examples/flow/main.go b/examples/flow/main.go index 5d9d7ef..40f09dd 100644 --- a/examples/flow/main.go +++ b/examples/flow/main.go @@ -21,7 +21,7 @@ func run () { world.Stages = map [string] func () { "start": func () { label := basic.NewLabel ( - "you are standing next to a river.", false) + "you are standing next to a river.", true) button0 := basic.NewButton("go in the river") button0.OnClick(world.SwitchFunc("wet")) @@ -41,7 +41,7 @@ func run () { "wet": func () { label := basic.NewLabel ( "you get completely soaked.\n" + - "you die of hypothermia.", false) + "you die of hypothermia.", true) button0 := basic.NewButton("try again") button0.OnClick(world.SwitchFunc("start")) @@ -58,7 +58,7 @@ func run () { "house": func () { label := basic.NewLabel ( "you are standing in front of a delapidated " + - "house.", false) + "house.", true) button1 := basic.NewButton("go inside") button1.OnClick(world.SwitchFunc("inside")) @@ -78,7 +78,7 @@ func run () { "it is dark, but rays of light stream " + "through the window.\n" + "there is nothing particularly interesting " + - "here.", false) + "here.", true) button0 := basic.NewButton("go back outside") button0.OnClick(world.SwitchFunc("house")) @@ -92,7 +92,7 @@ func run () { "bear": func () { label := basic.NewLabel ( "you come face to face with a bear.\n" + - "it eats you (it was hungry).", false) + "it eats you (it was hungry).", true) button0 := basic.NewButton("try again") button0.OnClick(world.SwitchFunc("start")) diff --git a/examples/label/main.go b/examples/label/main.go index 3e5611e..9a0f59b 100644 --- a/examples/label/main.go +++ b/examples/label/main.go @@ -9,7 +9,7 @@ func main () { } func run () { - window, _ := tomo.NewWindow(480, 360) + window, _ := tomo.NewWindow(480, 2) window.SetTitle("example label") window.Adopt(basic.NewLabel(text, true)) window.OnClose(tomo.Stop)