Skip to content

Commit ac0f903

Browse files
authored
Improving TextBox behaviour on Ctrl+Backspace down event (#14138)
* Test: TextBox Ctrl+Back caret position * Fixed: TextBox Ctrl+Back caret position * Test: TextBox Ctrl+Back remove double whitespace * Fixed: TextBox Ctrl+Back remove double whitespace * Test: TextBox Ctrl+Back undo return caret position * Fixed: TextBox Ctrl+Back undo return caret position
1 parent dac4ae4 commit ac0f903

File tree

2 files changed

+92
-9
lines changed

2 files changed

+92
-9
lines changed

src/Avalonia.Controls/TextBox.cs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,13 +1307,13 @@ protected override void OnKeyDown(KeyEventArgs e)
13071307
{
13081308
case Key.Left:
13091309
selection = DetectSelection();
1310-
MoveHorizontal(-1, hasWholeWordModifiers, selection);
1310+
MoveHorizontal(-1, hasWholeWordModifiers, selection, true);
13111311
movement = true;
13121312
break;
13131313

13141314
case Key.Right:
13151315
selection = DetectSelection();
1316-
MoveHorizontal(1, hasWholeWordModifiers, selection);
1316+
MoveHorizontal(1, hasWholeWordModifiers, selection, true);
13171317
movement = true;
13181318
break;
13191319

@@ -1781,7 +1781,7 @@ internal static int CoerceCaretIndex(AvaloniaObject sender, int value)
17811781
/// </summary>
17821782
public void Clear() => SetCurrentValue(TextProperty, string.Empty);
17831783

1784-
private void MoveHorizontal(int direction, bool wholeWord, bool isSelecting)
1784+
private void MoveHorizontal(int direction, bool wholeWord, bool isSelecting, bool moveCaretPosition)
17851785
{
17861786
if (_presenter == null)
17871787
{
@@ -1836,10 +1836,13 @@ private void MoveHorizontal(int direction, bool wholeWord, bool isSelecting)
18361836
}
18371837

18381838
SetCurrentValue(SelectionEndProperty, SelectionEnd + offset);
1839+
1840+
if (moveCaretPosition)
1841+
{
1842+
_presenter.MoveCaretToTextPosition(SelectionEnd);
1843+
}
18391844

1840-
_presenter.MoveCaretToTextPosition(SelectionEnd);
1841-
1842-
if (!isSelecting)
1845+
if (!isSelecting && moveCaretPosition)
18431846
{
18441847
SetCurrentValue(CaretIndexProperty, SelectionEnd);
18451848
}
@@ -1976,7 +1979,7 @@ internal bool DeleteSelection()
19761979

19771980
_presenter?.MoveCaretToTextPosition(start);
19781981

1979-
SetCurrentValue(CaretIndexProperty, start);
1982+
SetCurrentValue(SelectionStartProperty, start);
19801983

19811984
ClearSelection();
19821985

@@ -2066,9 +2069,16 @@ private void RaiseTextChangeEvents()
20662069

20672070
private void SetSelectionForControlBackspace()
20682071
{
2072+
var text = Text ?? string.Empty;
20692073
var selectionStart = CaretIndex;
20702074

2071-
MoveHorizontal(-1, true, false);
2075+
MoveHorizontal(-1, true, false, false);
2076+
2077+
if (SelectionEnd > 0 &&
2078+
selectionStart < text.Length && text[selectionStart] == ' ')
2079+
{
2080+
SetCurrentValue(SelectionEndProperty, SelectionEnd - 1);
2081+
}
20722082

20732083
SetCurrentValue(SelectionStartProperty, selectionStart);
20742084
}
@@ -2083,7 +2093,7 @@ private void SetSelectionForControlDelete()
20832093

20842094
SetCurrentValue(SelectionStartProperty, CaretIndex);
20852095

2086-
MoveHorizontal(1, true, true);
2096+
MoveHorizontal(1, true, true, false);
20872097

20882098
if (SelectionEnd < textLength && Text![SelectionEnd] == ' ')
20892099
{

tests/Avalonia.Controls.UnitTests/TextBoxTests.cs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,79 @@ public void CaretIndex_Can_Moved_To_Position_After_The_End_Of_Text_With_Arrow_Ke
151151
Assert.Equal(4, target.CaretIndex);
152152
}
153153
}
154+
155+
[Fact]
156+
public void Control_Backspace_Should_Set_Caret_Position_To_The_Start_Of_The_Deletion()
157+
{
158+
using (UnitTestApplication.Start(Services))
159+
{
160+
var target = new TextBox
161+
{
162+
Template = CreateTemplate(),
163+
Text = "First Second Third",
164+
SelectionStart = 13,
165+
SelectionEnd = 13
166+
};
167+
168+
target.CaretIndex = 10;
169+
target.ApplyTemplate();
170+
171+
// (First Second |Third)
172+
RaiseKeyEvent(target, Key.Back, KeyModifiers.Control);
173+
// (First |Third)
174+
175+
Assert.Equal(6, target.CaretIndex);
176+
}
177+
}
178+
179+
[Fact]
180+
public void Control_Backspace_Should_Remove_The_Double_Whitespace_If_Caret_Index_Was_At_The_End_Of_A_Word()
181+
{
182+
using (UnitTestApplication.Start(Services))
183+
{
184+
var target = new TextBox
185+
{
186+
Template = CreateTemplate(),
187+
Text = "First Second Third",
188+
SelectionStart = 12,
189+
SelectionEnd = 12
190+
};
191+
192+
target.ApplyTemplate();
193+
194+
// (First Second| Third)
195+
RaiseKeyEvent(target, Key.Back, KeyModifiers.Control);
196+
// (First| Third)
197+
198+
Assert.Equal("First Third", target.Text);
199+
}
200+
}
201+
202+
[Fact]
203+
public void Control_Backspace_Undo_Should_Return_Caret_Position()
204+
{
205+
using (UnitTestApplication.Start(Services))
206+
{
207+
var target = new TextBox
208+
{
209+
Template = CreateTemplate(),
210+
Text = "First Second Third",
211+
SelectionStart = 9,
212+
SelectionEnd = 9
213+
};
214+
215+
target.ApplyTemplate();
216+
217+
// (First Second| Third)
218+
RaiseKeyEvent(target, Key.Back, KeyModifiers.Control);
219+
// (First| Third)
220+
221+
target.Undo();
222+
// (First Second| Third)
223+
224+
Assert.Equal(9, target.CaretIndex);
225+
}
226+
}
154227

155228
[Fact]
156229
public void Press_Ctrl_A_Select_All_Text()

0 commit comments

Comments
 (0)