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 <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2021-11-29 17:13:39 +01:00
parent 665e23693f
commit ac97b9d6e1
3 changed files with 36 additions and 31 deletions
+7 -4
View File
@@ -462,8 +462,8 @@ func (e *Editor) Focused() bool {
return e.focused return e.focused
} }
// Layout lays out the editor. // 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) layout.Dimensions { 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)) textSize := fixed.I(gtx.Px(size))
if e.font != font || e.textSize != textSize { if e.font != font || e.textSize != textSize {
e.invalidate() e.invalidate()
@@ -497,10 +497,10 @@ func (e *Editor) Layout(gtx layout.Context, sh text.Shaper, font text.Font, size
} }
e.makeValid() 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. // Adjust scrolling for new viewport and layout.
e.scrollRel(0, 0) 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) 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} return layout.Dimensions{Size: e.viewSize, Baseline: e.dims.Baseline}
} }
+13 -13
View File
@@ -39,7 +39,7 @@ func TestEditor(t *testing.T) {
e.SetCaret(0, 0) // shouldn't panic e.SetCaret(0, 0) // shouldn't panic
assertCaret(t, e, 0, 0, 0) assertCaret(t, e, 0, 0, 0)
e.SetText("æbc\naøå•") e.SetText("æbc\naøå•")
e.Layout(gtx, cache, font, fontSize) e.Layout(gtx, cache, font, fontSize, nil)
assertCaret(t, e, 0, 0, 0) assertCaret(t, e, 0, 0, 0)
e.moveEnd(selectionClear) e.moveEnd(selectionClear)
assertCaret(t, e, 0, 3, len("æbc")) assertCaret(t, e, 0, 3, len("æbc"))
@@ -65,12 +65,12 @@ func TestEditor(t *testing.T) {
e.MoveCaret(-3, -3) e.MoveCaret(-3, -3)
assertCaret(t, e, 1, 1, len("æbc\na")) assertCaret(t, e, 1, 1, len("æbc\na"))
e.Mask = '*' e.Mask = '*'
e.Layout(gtx, cache, font, fontSize) e.Layout(gtx, cache, font, fontSize, nil)
assertCaret(t, e, 1, 1, len("æbc\na")) assertCaret(t, e, 1, 1, len("æbc\na"))
e.MoveCaret(-3, -3) e.MoveCaret(-3, -3)
assertCaret(t, e, 0, 2, len("æb")) assertCaret(t, e, 0, 2, len("æb"))
e.Mask = '\U0001F92B' e.Mask = '\U0001F92B'
e.Layout(gtx, cache, font, fontSize) e.Layout(gtx, cache, font, fontSize, nil)
e.moveEnd(selectionClear) e.moveEnd(selectionClear)
assertCaret(t, e, 0, 3, len("æbc")) assertCaret(t, e, 0, 3, len("æbc"))
@@ -99,7 +99,7 @@ func TestEditorDimensions(t *testing.T) {
cache := text.NewCache(gofont.Collection()) cache := text.NewCache(gofont.Collection())
fontSize := unit.Px(10) fontSize := unit.Px(10)
font := text.Font{} font := text.Font{}
dims := e.Layout(gtx, cache, font, fontSize) dims := e.Layout(gtx, cache, font, fontSize, nil)
if dims.Size.X == 0 { if dims.Size.X == 0 {
t.Errorf("EditEvent was not reflected in Editor width") t.Errorf("EditEvent was not reflected in Editor width")
} }
@@ -145,7 +145,7 @@ func TestEditorCaretConsistency(t *testing.T) {
e := &Editor{ e := &Editor{
Alignment: a, Alignment: a,
} }
e.Layout(gtx, cache, font, fontSize) e.Layout(gtx, cache, font, fontSize, nil)
consistent := func() error { consistent := func() error {
t.Helper() t.Helper()
@@ -167,7 +167,7 @@ func TestEditorCaretConsistency(t *testing.T) {
switch mutation { switch mutation {
case setText: case setText:
e.SetText(str) e.SetText(str)
e.Layout(gtx, cache, font, fontSize) e.Layout(gtx, cache, font, fontSize, nil)
case moveRune: case moveRune:
e.MoveCaret(int(distance), int(distance)) e.MoveCaret(int(distance), int(distance))
case moveLine: case moveLine:
@@ -232,7 +232,7 @@ func TestEditorMoveWord(t *testing.T) {
fontSize := unit.Px(10) fontSize := unit.Px(10)
font := text.Font{} font := text.Font{}
e.SetText(t) e.SetText(t)
e.Layout(gtx, cache, font, fontSize) e.Layout(gtx, cache, font, fontSize, nil)
return e return e
} }
for ii, tt := range tests { for ii, tt := range tests {
@@ -314,7 +314,7 @@ func TestEditorDeleteWord(t *testing.T) {
fontSize := unit.Px(10) fontSize := unit.Px(10)
font := text.Font{} font := text.Font{}
e.SetText(t) e.SetText(t)
e.Layout(gtx, cache, font, fontSize) e.Layout(gtx, cache, font, fontSize, nil)
return e return e
} }
for ii, tt := range tests { for ii, tt := range tests {
@@ -367,7 +367,7 @@ g123456789g
selected := func(start, end int) string { selected := func(start, end int) string {
// Layout once with no events; populate e.lines. // Layout once with no events; populate e.lines.
gtx.Queue = nil 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 _ = e.Events() // throw away any events from this layout
// Build the selection events // Build the selection events
@@ -389,7 +389,7 @@ g123456789g
} }
gtx.Queue = tq gtx.Queue = tq
e.Layout(gtx, cache, font, fontSize) e.Layout(gtx, cache, font, fontSize, nil)
for _, evt := range e.Events() { for _, evt := range e.Events() {
switch evt.(type) { switch evt.(type) {
case SelectEvent: case SelectEvent:
@@ -428,7 +428,7 @@ g123456789g
gtx.Constraints = layout.Exact(image.Pt(36, 36)) gtx.Constraints = layout.Exact(image.Pt(36, 36))
// Keep existing selection // Keep existing selection
gtx.Queue = nil 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 { if e.caret.end.lineCol != tst.startPos || e.caret.start.lineCol != tst.endPos {
t.Errorf("Test %d pt2: Expected %#v, %#v; got %#v, %#v", 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. // Layout once to populate e.lines and get focus.
gtx.Queue = newQueue(key.FocusEvent{Focus: true}) gtx.Queue = newQueue(key.FocusEvent{Focus: true})
e.Layout(gtx, cache, font, fontSize) e.Layout(gtx, cache, font, fontSize, nil)
testKey := func(keyName string) { testKey := func(keyName string) {
// Select 345 // Select 345
@@ -465,7 +465,7 @@ func TestSelectMove(t *testing.T) {
// Press the key // Press the key
gtx.Queue = newQueue(key.Event{State: key.Press, Name: keyName}) 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 { if expected, got := "", e.SelectedText(); expected != got {
t.Errorf("KeyName %s, expected %q, got %q", keyName, expected, got) t.Errorf("KeyName %s, expected %q, got %q", keyName, expected, got)
+16 -14
View File
@@ -58,20 +58,22 @@ func (e EditorStyle) Layout(gtx layout.Context) layout.Dimensions {
if h := dims.Size.Y; gtx.Constraints.Min.Y < h { if h := dims.Size.Y; gtx.Constraints.Min.Y < h {
gtx.Constraints.Min.Y = h gtx.Constraints.Min.Y = h
} }
dims = e.Editor.Layout(gtx, e.shaper, e.Font, e.TextSize) dims = e.Editor.Layout(gtx, e.shaper, e.Font, e.TextSize, func(gtx layout.Context) layout.Dimensions {
disabled := gtx.Queue == nil disabled := gtx.Queue == nil
if e.Editor.Len() > 0 { if e.Editor.Len() > 0 {
paint.ColorOp{Color: blendDisabledColor(disabled, e.SelectionColor)}.Add(gtx.Ops) paint.ColorOp{Color: blendDisabledColor(disabled, e.SelectionColor)}.Add(gtx.Ops)
e.Editor.PaintSelection(gtx) e.Editor.PaintSelection(gtx)
paint.ColorOp{Color: blendDisabledColor(disabled, e.Color)}.Add(gtx.Ops) paint.ColorOp{Color: blendDisabledColor(disabled, e.Color)}.Add(gtx.Ops)
e.Editor.PaintText(gtx) e.Editor.PaintText(gtx)
} else { } else {
call.Add(gtx.Ops) call.Add(gtx.Ops)
} }
if !disabled { if !disabled {
paint.ColorOp{Color: e.Color}.Add(gtx.Ops) paint.ColorOp{Color: e.Color}.Add(gtx.Ops)
e.Editor.PaintCaret(gtx) e.Editor.PaintCaret(gtx)
} }
return dims
})
return dims return dims
} }