From f7bc744a24bfdfecf1882d4ccc83e18fa139e5cb Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Tue, 26 Jul 2022 15:22:27 +0200 Subject: [PATCH] widget: add Editor.Filter for filtering unwanted characters Signed-off-by: Elias Naur --- widget/editor.go | 22 +++++++++++++++++----- widget/editor_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/widget/editor.go b/widget/editor.go index a319981a..d130e86f 100644 --- a/widget/editor.go +++ b/widget/editor.go @@ -49,6 +49,9 @@ type Editor struct { InputHint key.InputHint // MaxLen limits the editor content to a maximum length. Zero means no limit. MaxLen int + // Filter is the list of characters allowed in the Editor. If Filter is empty, + // all characters are allowed. + Filter string eventKey int font text.Font @@ -1235,12 +1238,21 @@ func (e *Editor) replace(start, end int, s string, addHistory bool) int { startPos := e.closestPosition(combinedPos{runes: start}) endPos := e.closestPosition(combinedPos{runes: end}) startOff := e.runeOffset(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-- + var sc int + idx := 0 + for idx < len(s) { + if e.MaxLen > 0 && el+sc >= e.MaxLen { + s = s[:idx] + break + } + _, n := utf8.DecodeRuneInString(s[idx:]) + if e.Filter != "" && !strings.Contains(e.Filter, s[idx:idx+n]) { + s = s[:idx] + s[idx+n:] + continue + } + idx += n + sc++ } newEnd := startPos.runes + sc replaceSize := endPos.runes - startPos.runes diff --git a/widget/editor_test.go b/widget/editor_test.go index 40d6f345..8926a690 100644 --- a/widget/editor_test.go +++ b/widget/editor_test.go @@ -1036,6 +1036,37 @@ func TestEditor_MaxLen(t *testing.T) { } } +func TestEditor_Filter(t *testing.T) { + e := new(Editor) + + e.Filter = "123456789" + e.SetText("abcde1234") + if got, want := e.Text(), "1234"; got != want { + t.Errorf("editor failed to filter 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: "ab1"}, + 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 filter EditEvent") + } + if start, end := e.Selection(); start != 2 || end != 2 { + 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