Skip to content

Commit

Permalink
Merge pull request #4662 from dweymouth/ctrl-backspace
Browse files Browse the repository at this point in the history
Support Ctrl+[backspace/delete] to delete the word to the left or right of the cursor
  • Loading branch information
dweymouth committed May 18, 2024
2 parents 6145be2 + 2490ce9 commit ecf8c68
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 1 deletion.
41 changes: 40 additions & 1 deletion widget/entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,37 @@ func (e *Entry) typedKeyEnd(provider *RichText) {
e.propertyLock.Unlock()
}

// handler for Ctrl+[backspace/delete] - delete the word
// to the left or right of the cursor
func (e *Entry) deleteWord(right bool) {
provider := e.textProvider()
cursorRow, cursorCol := e.CursorRow, e.CursorColumn

// start, end relative to text row
start, end := getTextWhitespaceRegion(provider.row(cursorRow), cursorCol, true)
if right {
start = cursorCol
} else {
end = cursorCol
}
if start == -1 || end == -1 {
return
}

// convert start, end to absolute text position
b := provider.rowBoundary(cursorRow)
if b != nil {
start += b.begin
end += b.begin
}

provider.deleteFromTo(start, end)
if !right {
e.CursorColumn = cursorCol - (end - start)
}
e.updateTextAndRefresh(provider.String(), false)
}

func (e *Entry) typedKeyTab() {
if dd, ok := fyne.CurrentApp().Driver().(desktop.Driver); ok {
if dd.CurrentKeyModifiers()&fyne.KeyModifierShift != 0 {
Expand Down Expand Up @@ -1214,6 +1245,11 @@ func (e *Entry) registerShortcut() {
e.shortcut.AddShortcut(&desktop.CustomShortcut{KeyName: fyne.KeyLeft, Modifier: moveWordModifier | fyne.KeyModifierShift}, selectMoveWord)
e.shortcut.AddShortcut(&desktop.CustomShortcut{KeyName: fyne.KeyRight, Modifier: moveWordModifier}, unselectMoveWord)
e.shortcut.AddShortcut(&desktop.CustomShortcut{KeyName: fyne.KeyRight, Modifier: moveWordModifier | fyne.KeyModifierShift}, selectMoveWord)

e.shortcut.AddShortcut(&desktop.CustomShortcut{KeyName: fyne.KeyBackspace, Modifier: moveWordModifier},
func(fyne.Shortcut) { e.deleteWord(false) })
e.shortcut.AddShortcut(&desktop.CustomShortcut{KeyName: fyne.KeyDelete, Modifier: moveWordModifier},
func(fyne.Shortcut) { e.deleteWord(true) })
}

func (e *Entry) requestFocus() {
Expand Down Expand Up @@ -2189,7 +2225,10 @@ func getTextWhitespaceRegion(row []rune, col int, expand bool) (int, int) {

// IndexByte will find the position of the next unwanted character, this is to be the end
// marker for the selection
end := strings.IndexByte(toks[endCheck:], c)
end := -1
if endCheck != -1 {
end = strings.IndexByte(toks[endCheck:], c)
}

if end == -1 {
end = len(toks) // snap end to len(toks) if it results in -1
Expand Down
32 changes: 32 additions & 0 deletions widget/entry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,38 @@ func TestEntry_Control_Word(t *testing.T) {
assert.Equal(t, "", entry.SelectedText())
}

func TestEntry_Control_DeleteWord(t *testing.T) {
entry := widget.NewMultiLineEntry()
entry.SetText("Hello world\nhere is a second line")
entry.CursorRow = 1
entry.CursorColumn = 10 // right before "second"
modifier := fyne.KeyModifierControl
if runtime.GOOS == "darwin" {
modifier = fyne.KeyModifierAlt
}
// Ctrl+delete - delete word to right ("second")
entry.TypedShortcut(&desktop.CustomShortcut{Modifier: modifier, KeyName: fyne.KeyDelete})
assert.Equal(t, "Hello world\nhere is a line", entry.Text)
assert.Equal(t, 10, entry.CursorColumn)

entry.CursorColumn = 8 // right before "a"
// Ctrl+backspace - delete word to left ("is")
entry.TypedShortcut(&desktop.CustomShortcut{Modifier: modifier, KeyName: fyne.KeyBackspace})
assert.Equal(t, "Hello world\nhere a line", entry.Text)
assert.Equal(t, 5, entry.CursorColumn)

// does nothing when nothing left to delete
entry.SetText("")
entry.TypedShortcut(&desktop.CustomShortcut{Modifier: modifier, KeyName: fyne.KeyBackspace})
assert.Equal(t, "", entry.Text)

// doesn't crash when trying to delete backward with one space
entry.SetText(" ")
entry.CursorRow = 0
entry.CursorColumn = 1
entry.TypedShortcut(&desktop.CustomShortcut{Modifier: modifier, KeyName: fyne.KeyBackspace})
}

func TestEntry_CursorColumn_Wrap(t *testing.T) {
entry := widget.NewMultiLineEntry()
entry.SetText("a\nb")
Expand Down

0 comments on commit ecf8c68

Please sign in to comment.