From 4996337d26cfb18c47b966e1235f2a1f4d50ed1e Mon Sep 17 00:00:00 2001 From: Chris Waldon Date: Fri, 6 May 2022 13:35:42 -0400 Subject: [PATCH] widget: ensure empty editor makes space for caret Prior to this change an editor with no content and a zero minimum constraint would return itself has having width zero. This prevented users from being able to see the editor when they moved focus to it, as it could not display its caret. This simple change ensures that, at minimum, the editor returns its dimensions to include the width of a caret. Signed-off-by: Chris Waldon --- widget/editor.go | 28 +++++++++++++++++++++++----- widget/editor_test.go | 20 ++++++++++++++++++++ 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/widget/editor.go b/widget/editor.go index 684520ec..026f5ee4 100644 --- a/widget/editor.go +++ b/widget/editor.go @@ -485,6 +485,17 @@ func (e *Editor) Focused() bool { return e.focused } +// calculateViewSize determines the size of the current visible content, +// ensuring that even if there is no text content, some space is reserved +// for the caret. +func (e *Editor) calculateViewSize(gtx layout.Context) image.Point { + base := e.dims.Size + if caretWidth := e.caretWidth(gtx); base.X < caretWidth { + base.X = caretWidth + } + return gtx.Constraints.Constrain(base) +} + // Layout lays out the editor. If content is not nil, it is laid out on top. func (e *Editor) Layout(gtx layout.Context, sh text.Shaper, font text.Font, size unit.Value, content layout.Widget) layout.Dimensions { if e.locale != gtx.Locale { @@ -518,7 +529,7 @@ func (e *Editor) Layout(gtx layout.Context, sh text.Shaper, font text.Font, size e.processEvents(gtx) e.makeValid() - if viewSize := gtx.Constraints.Constrain(e.dims.Size); viewSize != e.viewSize { + if viewSize := e.calculateViewSize(gtx); viewSize != e.viewSize { e.viewSize = viewSize e.invalidate() } @@ -749,14 +760,21 @@ func (e *Editor) PaintText(gtx layout.Context) { } } -func (e *Editor) PaintCaret(gtx layout.Context) { - if !e.caret.on { - return - } +// caretWidth returns the width occupied by the caret for the current +// gtx. +func (e *Editor) caretWidth(gtx layout.Context) int { carWidth2 := gtx.Px(unit.Dp(1)) / 2 if carWidth2 < 1 { carWidth2 = 1 } + return carWidth2 +} + +func (e *Editor) PaintCaret(gtx layout.Context) { + if !e.caret.on { + return + } + carWidth2 := e.caretWidth(gtx) caretPos, carAsc, carDesc := e.caretInfo() carRect := image.Rectangle{ diff --git a/widget/editor_test.go b/widget/editor_test.go index cec9bc9b..1344ca27 100644 --- a/widget/editor_test.go +++ b/widget/editor_test.go @@ -36,6 +36,26 @@ var english = system.Locale{ Direction: system.LTR, } +// TestEditorZeroDimensions ensures that an empty editor still reserves +// space for displaying its caret when the constraints allow for it. +func TestEditorZeroDimensions(t *testing.T) { + gtx := layout.Context{ + Ops: new(op.Ops), + Constraints: layout.Constraints{ + Max: image.Pt(100, 100), + }, + Locale: english, + } + cache := text.NewCache(gofont.Collection()) + fontSize := unit.Px(10) + font := text.Font{} + e := new(Editor) + dims := e.Layout(gtx, cache, font, fontSize, nil) + if dims.Size.X < 1 || dims.Size.Y < 1 { + t.Errorf("expected empty editor to occupy enough space to display cursor, but returned dimensions %v", dims) + } +} + func TestEditorConfigurations(t *testing.T) { gtx := layout.Context{ Ops: new(op.Ops),