From fe5878bc63fd41c40c202e8eff087bc3a37d50ef Mon Sep 17 00:00:00 2001 From: Chris Waldon Date: Mon, 19 Dec 2022 10:09:00 -0500 Subject: [PATCH] widget: track minWidth of editor for alignment This commit extends the editor to keep track of its own minimum constraint and to provide that value to the text shaper for the purpose of aligning text. Without this, the shaper does not know how much of the width of the editor to use for alignment purposes. Signed-off-by: Chris Waldon --- widget/editor.go | 37 ++++++++++++++++------------- widget/editor_test.go | 54 +++++++++++++++++++++++++++---------------- 2 files changed, 55 insertions(+), 36 deletions(-) diff --git a/widget/editor.go b/widget/editor.go index e8e7d7a6..9ae07d7b 100644 --- a/widget/editor.go +++ b/widget/editor.go @@ -52,21 +52,21 @@ type Editor struct { // all characters are allowed. Filter string - eventKey int - font text.Font - shaper *text.Shaper - textSize fixed.Int26_6 - blinkStart time.Time - focused bool - rr editBuffer - maskReader maskReader - lastMask rune - maxWidth int - viewSize image.Point - valid bool - regions []region - dims layout.Dimensions - requestFocus bool + eventKey int + font text.Font + shaper *text.Shaper + textSize fixed.Int26_6 + blinkStart time.Time + focused bool + rr editBuffer + maskReader maskReader + lastMask rune + maxWidth, minWidth int + viewSize image.Point + valid bool + regions []region + dims layout.Dimensions + requestFocus bool // offIndex is an index of rune index to byte offsets. offIndex []offEntry @@ -547,10 +547,15 @@ func (e *Editor) Layout(gtx layout.Context, lt *text.Shaper, font text.Font, siz if e.SingleLine { maxWidth = math.MaxInt } + minWidth := gtx.Constraints.Min.X if maxWidth != e.maxWidth { e.maxWidth = maxWidth e.invalidate() } + if minWidth != e.minWidth { + e.minWidth = minWidth + e.invalidate() + } if lt != e.shaper { e.shaper = lt e.invalidate() @@ -891,7 +896,7 @@ func (e *Editor) layoutText(lt *text.Shaper) { Font: e.font, PxPerEm: e.textSize, Alignment: e.Alignment, - }, 0, e.maxWidth, e.locale, r) + }, e.minWidth, e.maxWidth, e.locale, r) for glyph, ok := it.processGlyph(lt.NextGlyph()); ok; glyph, ok = it.processGlyph(lt.NextGlyph()) { e.index.Glyph(glyph) } diff --git a/widget/editor_test.go b/widget/editor_test.go index d82a4355..703e2e5a 100644 --- a/widget/editor_test.go +++ b/widget/editor_test.go @@ -115,7 +115,7 @@ func TestEditorZeroDimensions(t *testing.T) { func TestEditorConfigurations(t *testing.T) { gtx := layout.Context{ Ops: new(op.Ops), - Constraints: layout.Exact(image.Pt(100, 100)), + Constraints: layout.Exact(image.Pt(300, 300)), Locale: english, } cache := text.NewShaper(gofont.Collection()) @@ -126,27 +126,41 @@ func TestEditorConfigurations(t *testing.T) { // Ensure that both ends of the text are reachable in all permutations // of settings that influence layout. - for _, lineMode := range []bool{true, false} { + for _, singleLine := range []bool{true, false} { for _, alignment := range []text.Alignment{text.Start, text.Middle, text.End} { - t.Run(fmt.Sprintf("SingleLine: %v Alignment: %v", lineMode, alignment), func(t *testing.T) { - defer func() { - if err := recover(); err != nil { - t.Error(err) + for _, zeroMin := range []bool{true, false} { + t.Run(fmt.Sprintf("SingleLine: %v Alignment: %v ZeroMinConstraint: %v", singleLine, alignment, zeroMin), func(t *testing.T) { + defer func() { + if err := recover(); err != nil { + t.Error(err) + } + }() + if zeroMin { + gtx.Constraints.Min = image.Point{} + } else { + gtx.Constraints.Min = gtx.Constraints.Max } - }() - e := new(Editor) - e.SingleLine = lineMode - e.Alignment = alignment - e.SetText(sentence) - e.SetCaret(0, 0) - e.Layout(gtx, cache, font, fontSize, nil) - e.SetCaret(runes, runes) - e.Layout(gtx, cache, font, fontSize, nil) - coords := e.CaretCoords() - if int(coords.X) > gtx.Constraints.Max.X || int(coords.Y) > gtx.Constraints.Max.Y { - t.Errorf("caret coordinates %v exceed constraints %v", coords, gtx.Constraints.Max) - } - }) + e := new(Editor) + e.SingleLine = singleLine + e.Alignment = alignment + e.SetText(sentence) + e.SetCaret(0, 0) + dims := e.Layout(gtx, cache, font, fontSize, nil) + if dims.Size.X < gtx.Constraints.Min.X || dims.Size.Y < gtx.Constraints.Min.Y { + t.Errorf("expected min size %#+v, got %#+v", gtx.Constraints.Min, dims.Size) + } + coords := e.CaretCoords() + if halfway := float32(gtx.Constraints.Min.X) * .5; !singleLine && alignment == text.Middle && !zeroMin && coords.X != halfway { + t.Errorf("expected caret X to be %f, got %f", halfway, coords.X) + } + e.SetCaret(runes, runes) + e.Layout(gtx, cache, font, fontSize, nil) + coords = e.CaretCoords() + if int(coords.X) > gtx.Constraints.Max.X || int(coords.Y) > gtx.Constraints.Max.Y { + t.Errorf("caret coordinates %v exceed constraints %v", coords, gtx.Constraints.Max) + } + }) + } } } }