From 595c1fb71bb3d9070d94e4a77eeffc139f13ef37 Mon Sep 17 00:00:00 2001 From: Jacob Date: Tue, 27 Feb 2024 10:33:01 +0100 Subject: [PATCH] Rewrite selection erase to not allocate internal string buffer twice --- widget/entry.go | 61 +++++++++++++++++++++++------------ widget/entry_internal_test.go | 2 +- 2 files changed, 42 insertions(+), 21 deletions(-) diff --git a/widget/entry.go b/widget/entry.go index 49f23fe559..1478acafdf 100644 --- a/widget/entry.go +++ b/widget/entry.go @@ -901,16 +901,13 @@ func (e *Entry) TypedRune(r rune) { // if we've typed a character and we're selecting then replace the selection with the character cb := e.OnChanged if e.selecting { - e.OnChanged = nil // don't propagate this change to binding etc e.eraseSelection() - e.OnChanged = cb // the change later will then trigger callback } - provider := e.textProvider() - e.selecting = false - runes := []rune{r} pos := e.cursorTextPos() + + provider := e.textProvider() provider.insertAt(pos, string(runes)) content := provider.String() @@ -985,7 +982,7 @@ func (e *Entry) cutToClipboard(clipboard fyne.Clipboard) { e.copyToClipboard(clipboard) e.propertyLock.Lock() - e.eraseSelection() + e.eraseSelectionAndUpdate() content := e.Text cb := e.OnChanged e.propertyLock.Unlock() @@ -997,17 +994,17 @@ func (e *Entry) cutToClipboard(clipboard fyne.Clipboard) { e.Refresh() } -// eraseSelection removes the current selected region and moves the cursor -func (e *Entry) eraseSelection() { +// eraseSelection deletes the selected text and moves the cursor but does not update the text field. +func (e *Entry) eraseSelection() bool { if e.Disabled() { - return + return false } provider := e.textProvider() posA, posB := e.selection() if posA == posB { - return + return false } erasedText := e.Text[posA:posB] @@ -1016,13 +1013,22 @@ func (e *Entry) eraseSelection() { e.CursorRow, e.CursorColumn = e.rowColFromTextPos(posA) e.selectRow, e.selectColumn = e.CursorRow, e.CursorColumn e.selecting = false - e.updateText(provider.String(), false) e.undoStack.MergeOrAdd(&entryModifyAction{ Delete: true, Position: posA, Text: erasedText, }) + + return true +} + +// eraseSelectionAndUpdate removes the current selected region and moves the cursor. +// It also updates the text if something has been erased. +func (e *Entry) eraseSelectionAndUpdate() { + if e.eraseSelection() { + e.updateText(e.textProvider().String(), false) + } } func (e *Entry) getRowCol(p fyne.Position) (int, int) { @@ -1049,12 +1055,16 @@ func (e *Entry) getRowCol(p fyne.Position) (int, int) { // pasteFromClipboard inserts text from the clipboard content, // starting from the cursor position. func (e *Entry) pasteFromClipboard(clipboard fyne.Clipboard) { - if e.selecting { - e.SetFieldsAndRefresh(e.eraseSelection) - } - text := clipboard.Content() if text == "" { + e.propertyLock.Lock() + changed := e.selecting && e.eraseSelection() + e.propertyLock.Unlock() + + if changed { + e.Refresh() + } + return // Nothing to paste into the text content. } @@ -1062,20 +1072,31 @@ func (e *Entry) pasteFromClipboard(clipboard fyne.Clipboard) { // format clipboard content to be compatible with single line entry text = strings.Replace(text, "\n", " ", -1) } - provider := e.textProvider() + + e.propertyLock.Lock() + if e.selecting { + e.eraseSelection() + } + runes := []rune(text) pos := e.cursorTextPos() + provider := e.textProvider() provider.insertAt(pos, text) - e.propertyLock.Lock() e.undoStack.Add(&entryModifyAction{ Position: pos, Text: text, }) - e.updateText(provider.String(), false) + content := provider.String() + e.updateText(content, false) e.CursorRow, e.CursorColumn = e.rowColFromTextPos(pos + len(runes)) + cb := e.OnChanged e.propertyLock.Unlock() + if cb != nil { + cb(content) // We know that the text has changed. + } + e.Refresh() // placing the cursor (and refreshing) happens last } @@ -1267,7 +1288,7 @@ func (e *Entry) selectingKeyHandler(key *fyne.KeyEvent) bool { case fyne.KeyBackspace, fyne.KeyDelete: // clears the selection -- return handled e.propertyLock.Lock() - e.eraseSelection() + e.eraseSelectionAndUpdate() content := e.Text cb := e.OnChanged e.propertyLock.Unlock() @@ -1281,7 +1302,7 @@ func (e *Entry) selectingKeyHandler(key *fyne.KeyEvent) bool { case fyne.KeyReturn, fyne.KeyEnter: if e.MultiLine { // clear the selection -- return unhandled to add the newline - e.SetFieldsAndRefresh(e.eraseSelection) + e.SetFieldsAndRefresh(e.eraseSelectionAndUpdate) } return false } diff --git a/widget/entry_internal_test.go b/widget/entry_internal_test.go index 8ff5806e64..57313ca48f 100644 --- a/widget/entry_internal_test.go +++ b/widget/entry_internal_test.go @@ -280,7 +280,7 @@ func TestEntry_EraseSelection(t *testing.T) { keyPress(&fyne.KeyEvent{Name: fyne.KeyRight}) _ = e.Theme() - e.eraseSelection() + e.eraseSelectionAndUpdate() e.updateText(e.textProvider().String(), false) assert.Equal(t, "Testing\nTeng\nTesting", e.Text) a, b := e.selection()