diff --git a/widget/entry.go b/widget/entry.go index ea732543dd..6308bb5225 100644 --- a/widget/entry.go +++ b/widget/entry.go @@ -908,6 +908,53 @@ func (e *Entry) Unbind() { e.binder.Unbind() } +// InsertAtCursor inserts the given text into the Entry at the current cursor position. +// If text is currently selected, the selected text will be replaced +// with the inserted content. +// +// Since: 2.6 +func (e *Entry) InsertAtCursor(text string) { + if text == "" { + changed := e.selecting && e.eraseSelection() + + if changed { + e.Refresh() + } + + return // Nothing to paste into the text content. + } + + if !e.MultiLine { + // format clipboard content to be compatible with single line entry + text = strings.Replace(text, "\n", " ", -1) + } + + if e.selecting { + e.eraseSelection() + } + + runes := []rune(text) + pos := e.cursorTextPos() + provider := e.textProvider() + provider.insertAt(pos, runes) + + e.undoStack.Add(&entryModifyAction{ + Position: pos, + Text: runes, + }) + content := provider.String() + e.updateText(content, false) + e.CursorRow, e.CursorColumn = e.rowColFromTextPos(pos + len(runes)) + cb := e.OnChanged + + e.validate() + if cb != nil { + cb(content) // We know that the text has changed. + } + + e.Refresh() // placing the cursor (and refreshing) happens last} +} + // copyToClipboard copies the current selection to a given clipboard. // This does nothing if it is a concealed entry. func (e *Entry) copyToClipboard(clipboard fyne.Clipboard) { @@ -1014,46 +1061,7 @@ 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) { - text := clipboard.Content() - if text == "" { - changed := e.selecting && e.eraseSelection() - - if changed { - e.Refresh() - } - - return // Nothing to paste into the text content. - } - - if !e.MultiLine { - // format clipboard content to be compatible with single line entry - text = strings.Replace(text, "\n", " ", -1) - } - - if e.selecting { - e.eraseSelection() - } - - runes := []rune(text) - pos := e.cursorTextPos() - provider := e.textProvider() - provider.insertAt(pos, runes) - - e.undoStack.Add(&entryModifyAction{ - Position: pos, - Text: runes, - }) - content := provider.String() - e.updateText(content, false) - e.CursorRow, e.CursorColumn = e.rowColFromTextPos(pos + len(runes)) - cb := e.OnChanged - - e.validate() - if cb != nil { - cb(content) // We know that the text has changed. - } - - e.Refresh() // placing the cursor (and refreshing) happens last + e.InsertAtCursor(clipboard.Content()) } // placeholderProvider returns the placeholder text handler for this entry diff --git a/widget/entry_test.go b/widget/entry_test.go index 1b11b34b93..97c2a34081 100644 --- a/widget/entry_test.go +++ b/widget/entry_test.go @@ -946,6 +946,82 @@ func TestEntry_OnPaste(t *testing.T) { } } +func TestEntry_InsertAtCursor(t *testing.T) { + tests := []struct { + name string + entry *widget.Entry + insertText string + wantText string + wantRow, wantCol int + }{ + { + name: "singleline: empty content", + entry: widget.NewEntry(), + insertText: "", + wantText: "", + wantRow: 0, + wantCol: 0, + }, + { + name: "singleline: simple text", + entry: widget.NewEntry(), + insertText: "clipboard content", + wantText: "clipboard content", + wantRow: 0, + wantCol: 17, + }, + { + name: "singleline: UTF8 text", + entry: widget.NewEntry(), + insertText: "Hié™שרה", + wantText: "Hié™שרה", + wantRow: 0, + wantCol: 7, + }, + { + name: "singleline: with new line", + entry: widget.NewEntry(), + insertText: "clipboard\ncontent", + wantText: "clipboard content", + wantRow: 0, + wantCol: 17, + }, + { + name: "singleline: with tab", + entry: widget.NewEntry(), + insertText: "clipboard\tcontent", + wantText: "clipboard\tcontent", + wantRow: 0, + wantCol: 17, + }, + { + name: "password: with new line", + entry: widget.NewPasswordEntry(), + insertText: "3SB=y+)z\nkHGK(hx6 -e_\"1TZu q^bF3^$u H[:e\"1O.", + wantText: `3SB=y+)z kHGK(hx6 -e_"1TZu q^bF3^$u H[:e"1O.`, + wantRow: 0, + wantCol: 44, + }, + { + name: "multiline: with new line", + entry: widget.NewMultiLineEntry(), + insertText: "clipboard\ncontent", + wantText: "clipboard\ncontent", + wantRow: 1, + wantCol: 7, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.entry.InsertAtCursor(tt.insertText) + assert.Equal(t, tt.wantText, tt.entry.Text) + assert.Equal(t, tt.wantRow, tt.entry.CursorRow) + assert.Equal(t, tt.wantCol, tt.entry.CursorColumn) + }) + } +} + func TestEntry_PageUpDown(t *testing.T) { t.Run("single line", func(*testing.T) { e, window := setupImageTest(t, false)