From ac97b9d6e13cf9cd1ba0ef9f2693f4aa3774a8d7 Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Mon, 29 Nov 2021 17:13:39 +0100 Subject: [PATCH] widget: [API] add content widget argument to Editor.Layout To make the semantic relation between the editor and its content clear, the editor clip operation must cover the content. This change adds an explicit widget argument to editor, and lays it out inside the clip rect. This is an API change. Users of Editor.Layout must provide a content widget. Signed-off-by: Elias Naur --- widget/editor.go | 11 +++++++---- widget/editor_test.go | 26 +++++++++++++------------- widget/material/editor.go | 30 ++++++++++++++++-------------- 3 files changed, 36 insertions(+), 31 deletions(-) diff --git a/widget/editor.go b/widget/editor.go index 9bc656fa..31045f25 100644 --- a/widget/editor.go +++ b/widget/editor.go @@ -462,8 +462,8 @@ func (e *Editor) Focused() bool { return e.focused } -// Layout lays out the editor. -func (e *Editor) Layout(gtx layout.Context, sh text.Shaper, font text.Font, size unit.Value) layout.Dimensions { +// 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 { textSize := fixed.I(gtx.Px(size)) if e.font != font || e.textSize != textSize { e.invalidate() @@ -497,10 +497,10 @@ func (e *Editor) Layout(gtx layout.Context, sh text.Shaper, font text.Font, size } e.makeValid() - return e.layout(gtx) + return e.layout(gtx, content) } -func (e *Editor) layout(gtx layout.Context) layout.Dimensions { +func (e *Editor) layout(gtx layout.Context, content layout.Widget) layout.Dimensions { // Adjust scrolling for new viewport and layout. e.scrollRel(0, 0) @@ -576,6 +576,9 @@ func (e *Editor) layout(gtx layout.Context) layout.Dimensions { e.caret.on = e.focused && (!blinking || dt%timePerBlink < timePerBlink/2) } + if content != nil { + content(gtx) + } return layout.Dimensions{Size: e.viewSize, Baseline: e.dims.Baseline} } diff --git a/widget/editor_test.go b/widget/editor_test.go index 56e76311..d66192eb 100644 --- a/widget/editor_test.go +++ b/widget/editor_test.go @@ -39,7 +39,7 @@ func TestEditor(t *testing.T) { e.SetCaret(0, 0) // shouldn't panic assertCaret(t, e, 0, 0, 0) e.SetText("æbc\naøå•") - e.Layout(gtx, cache, font, fontSize) + e.Layout(gtx, cache, font, fontSize, nil) assertCaret(t, e, 0, 0, 0) e.moveEnd(selectionClear) assertCaret(t, e, 0, 3, len("æbc")) @@ -65,12 +65,12 @@ func TestEditor(t *testing.T) { e.MoveCaret(-3, -3) assertCaret(t, e, 1, 1, len("æbc\na")) e.Mask = '*' - e.Layout(gtx, cache, font, fontSize) + e.Layout(gtx, cache, font, fontSize, nil) assertCaret(t, e, 1, 1, len("æbc\na")) e.MoveCaret(-3, -3) assertCaret(t, e, 0, 2, len("æb")) e.Mask = '\U0001F92B' - e.Layout(gtx, cache, font, fontSize) + e.Layout(gtx, cache, font, fontSize, nil) e.moveEnd(selectionClear) assertCaret(t, e, 0, 3, len("æbc")) @@ -99,7 +99,7 @@ func TestEditorDimensions(t *testing.T) { cache := text.NewCache(gofont.Collection()) fontSize := unit.Px(10) font := text.Font{} - dims := e.Layout(gtx, cache, font, fontSize) + dims := e.Layout(gtx, cache, font, fontSize, nil) if dims.Size.X == 0 { t.Errorf("EditEvent was not reflected in Editor width") } @@ -145,7 +145,7 @@ func TestEditorCaretConsistency(t *testing.T) { e := &Editor{ Alignment: a, } - e.Layout(gtx, cache, font, fontSize) + e.Layout(gtx, cache, font, fontSize, nil) consistent := func() error { t.Helper() @@ -167,7 +167,7 @@ func TestEditorCaretConsistency(t *testing.T) { switch mutation { case setText: e.SetText(str) - e.Layout(gtx, cache, font, fontSize) + e.Layout(gtx, cache, font, fontSize, nil) case moveRune: e.MoveCaret(int(distance), int(distance)) case moveLine: @@ -232,7 +232,7 @@ func TestEditorMoveWord(t *testing.T) { fontSize := unit.Px(10) font := text.Font{} e.SetText(t) - e.Layout(gtx, cache, font, fontSize) + e.Layout(gtx, cache, font, fontSize, nil) return e } for ii, tt := range tests { @@ -314,7 +314,7 @@ func TestEditorDeleteWord(t *testing.T) { fontSize := unit.Px(10) font := text.Font{} e.SetText(t) - e.Layout(gtx, cache, font, fontSize) + e.Layout(gtx, cache, font, fontSize, nil) return e } for ii, tt := range tests { @@ -367,7 +367,7 @@ g123456789g selected := func(start, end int) string { // Layout once with no events; populate e.lines. gtx.Queue = nil - e.Layout(gtx, cache, font, fontSize) + e.Layout(gtx, cache, font, fontSize, nil) _ = e.Events() // throw away any events from this layout // Build the selection events @@ -389,7 +389,7 @@ g123456789g } gtx.Queue = tq - e.Layout(gtx, cache, font, fontSize) + e.Layout(gtx, cache, font, fontSize, nil) for _, evt := range e.Events() { switch evt.(type) { case SelectEvent: @@ -428,7 +428,7 @@ g123456789g gtx.Constraints = layout.Exact(image.Pt(36, 36)) // Keep existing selection gtx.Queue = nil - e.Layout(gtx, cache, font, fontSize) + e.Layout(gtx, cache, font, fontSize, nil) if e.caret.end.lineCol != tst.startPos || e.caret.start.lineCol != tst.endPos { t.Errorf("Test %d pt2: Expected %#v, %#v; got %#v, %#v", @@ -454,7 +454,7 @@ func TestSelectMove(t *testing.T) { // Layout once to populate e.lines and get focus. gtx.Queue = newQueue(key.FocusEvent{Focus: true}) - e.Layout(gtx, cache, font, fontSize) + e.Layout(gtx, cache, font, fontSize, nil) testKey := func(keyName string) { // Select 345 @@ -465,7 +465,7 @@ func TestSelectMove(t *testing.T) { // Press the key gtx.Queue = newQueue(key.Event{State: key.Press, Name: keyName}) - e.Layout(gtx, cache, font, fontSize) + e.Layout(gtx, cache, font, fontSize, nil) if expected, got := "", e.SelectedText(); expected != got { t.Errorf("KeyName %s, expected %q, got %q", keyName, expected, got) diff --git a/widget/material/editor.go b/widget/material/editor.go index 690793a8..ff713e20 100644 --- a/widget/material/editor.go +++ b/widget/material/editor.go @@ -58,20 +58,22 @@ func (e EditorStyle) Layout(gtx layout.Context) layout.Dimensions { if h := dims.Size.Y; gtx.Constraints.Min.Y < h { gtx.Constraints.Min.Y = h } - dims = e.Editor.Layout(gtx, e.shaper, e.Font, e.TextSize) - disabled := gtx.Queue == nil - if e.Editor.Len() > 0 { - paint.ColorOp{Color: blendDisabledColor(disabled, e.SelectionColor)}.Add(gtx.Ops) - e.Editor.PaintSelection(gtx) - paint.ColorOp{Color: blendDisabledColor(disabled, e.Color)}.Add(gtx.Ops) - e.Editor.PaintText(gtx) - } else { - call.Add(gtx.Ops) - } - if !disabled { - paint.ColorOp{Color: e.Color}.Add(gtx.Ops) - e.Editor.PaintCaret(gtx) - } + dims = e.Editor.Layout(gtx, e.shaper, e.Font, e.TextSize, func(gtx layout.Context) layout.Dimensions { + disabled := gtx.Queue == nil + if e.Editor.Len() > 0 { + paint.ColorOp{Color: blendDisabledColor(disabled, e.SelectionColor)}.Add(gtx.Ops) + e.Editor.PaintSelection(gtx) + paint.ColorOp{Color: blendDisabledColor(disabled, e.Color)}.Add(gtx.Ops) + e.Editor.PaintText(gtx) + } else { + call.Add(gtx.Ops) + } + if !disabled { + paint.ColorOp{Color: e.Color}.Add(gtx.Ops) + e.Editor.PaintCaret(gtx) + } + return dims + }) return dims }