From 1d9ab6531396466de17614cfbb21b04ec6423b4a Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Wed, 13 Jul 2022 18:45:02 +0200 Subject: [PATCH] widget: add Editor.MaxLen for limiting the content length og Editor Signed-off-by: Elias Naur --- widget/editor.go | 27 ++++++++++++++++++++++----- widget/editor_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/widget/editor.go b/widget/editor.go index 34164082..35149acd 100644 --- a/widget/editor.go +++ b/widget/editor.go @@ -47,6 +47,8 @@ type Editor struct { Mask rune // InputHint specifies the type of on-screen keyboard to be displayed. InputHint key.InputHint + // MaxLen limits the editor content to a maximum length. Zero means no limit. + MaxLen int eventKey int font text.Font @@ -334,6 +336,8 @@ func (e *Editor) processKey(gtx layout.Context) { if e.rr.Changed() { e.events = append(e.events, ChangeEvent{}) } + // adjust keeps track of runes dropped because of MaxLen. + var adjust int for _, ke := range gtx.Events(&e.eventKey) { e.blinkStart = gtx.Now switch ke := ke.(type) { @@ -361,7 +365,8 @@ func (e *Editor) processKey(gtx layout.Context) { case key.EditEvent: e.caret.scroll = true e.scroller.Stop() - e.replace(ke.Range.Start, ke.Range.End, ke.Text) + moves := e.replace(ke.Range.Start, ke.Range.End, ke.Text) + adjust += utf8.RuneCountInString(ke.Text) - moves e.caret.xoff = 0 // Complete a paste event, initiated by Shortcut-V in Editor.command(). case clipboard.Event: @@ -371,6 +376,9 @@ func (e *Editor) processKey(gtx layout.Context) { case key.SelectionEvent: e.caret.scroll = true e.scroller.Stop() + ke.Start -= adjust + ke.End -= adjust + adjust = 0 e.caret.start = e.closestPosition(combinedPos{runes: ke.Start}).runes e.caret.end = e.closestPosition(combinedPos{runes: ke.End}).runes } @@ -1147,18 +1155,19 @@ func (e *Editor) Insert(s string) { // there is a selection, append overwrites it. // xxx|yyy + append zzz => xxxzzz|yyy func (e *Editor) append(s string) { - e.replace(e.caret.start, e.caret.end, s) + moves := e.replace(e.caret.start, e.caret.end, s) e.caret.xoff = 0 start := e.caret.start if end := e.caret.end; end < start { start = end } - e.caret.start = start + utf8.RuneCountInString(s) + e.caret.start = start + moves e.caret.end = e.caret.start } // replace the text between start and end with s. Indices are in runes. -func (e *Editor) replace(start, end int, s string) { +// It returns the number of runes inserted. +func (e *Editor) replace(start, end int, s string) int { if e.SingleLine { s = strings.ReplaceAll(s, "\n", " ") } @@ -1169,8 +1178,15 @@ func (e *Editor) replace(start, end int, s string) { endPos := e.closestPosition(combinedPos{runes: end}) startOff := e.runeOffset(startPos.runes) e.rr.deleteRunes(startOff, endPos.runes-startPos.runes) + sc := utf8.RuneCountInString(s) + el := e.Len() + for e.MaxLen > 0 && el+sc > e.MaxLen { + _, n := utf8.DecodeLastRuneInString(s) + s = s[:len(s)-n] + sc-- + } e.rr.prepend(startOff, s) - newEnd := startPos.runes + utf8.RuneCountInString(s) + newEnd := startPos.runes + sc adjust := func(pos int) int { switch { case newEnd < pos && pos <= endPos.runes: @@ -1186,6 +1202,7 @@ func (e *Editor) replace(start, end int, s string) { e.ime.start = adjust(e.ime.start) e.ime.end = adjust(e.ime.end) e.invalidate() + return sc } func (e *Editor) movePages(pages int, selAct selectionAction) { diff --git a/widget/editor_test.go b/widget/editor_test.go index e1f45e33..454c0c70 100644 --- a/widget/editor_test.go +++ b/widget/editor_test.go @@ -947,6 +947,37 @@ func TestEditor_WriteTo(t *testing.T) { } } +func TestEditor_MaxLen(t *testing.T) { + e := new(Editor) + + e.MaxLen = 8 + e.SetText("123456789") + if got, want := e.Text(), "12345678"; got != want { + t.Errorf("editor failed to cap SetText") + } + + e.SetText("2345678") + gtx := layout.Context{ + Ops: new(op.Ops), + Constraints: layout.Exact(image.Pt(100, 100)), + Queue: newQueue( + key.EditEvent{Range: key.Range{Start: 0, End: 0}, Text: "1234"}, + key.SelectionEvent{Start: 4, End: 4}, + ), + } + cache := text.NewCache(gofont.Collection()) + fontSize := unit.Sp(10) + font := text.Font{} + e.Layout(gtx, cache, font, fontSize, nil) + + if got, want := e.Text(), "12345678"; got != want { + t.Errorf("editor failed to cap EditEvent") + } + if start, end := e.Selection(); start != 1 || end != 1 { + t.Errorf("editor failed to adjust SelectionEvent") + } +} + func textWidth(e *Editor, lineNum, colStart, colEnd int) float32 { var w fixed.Int26_6 glyphs := e.lines[lineNum].Layout.Glyphs -- 2.38.5