From 6b5a9a7ccdd6d91313185f6037d93ef309fabe8c Mon Sep 17 00:00:00 2001 From: exGensImpl Date: Tue, 9 Jan 2024 19:09:06 +0300 Subject: [PATCH] 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 --- src/Avalonia.Controls/TextBox.cs | 28 ++++--- .../TextBoxTests.cs | 73 +++++++++++++++++++ 2 files changed, 92 insertions(+), 9 deletions(-) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index d0952e90d27..9ae63941f73 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -1275,13 +1275,13 @@ protected override void OnKeyDown(KeyEventArgs e) { case Key.Left: selection = DetectSelection(); - MoveHorizontal(-1, hasWholeWordModifiers, selection); + MoveHorizontal(-1, hasWholeWordModifiers, selection, true); movement = true; break; case Key.Right: selection = DetectSelection(); - MoveHorizontal(1, hasWholeWordModifiers, selection); + MoveHorizontal(1, hasWholeWordModifiers, selection, true); movement = true; break; @@ -1672,7 +1672,7 @@ internal static int CoerceCaretIndex(AvaloniaObject sender, int value) /// public void Clear() => SetCurrentValue(TextProperty, string.Empty); - private void MoveHorizontal(int direction, bool wholeWord, bool isSelecting) + private void MoveHorizontal(int direction, bool wholeWord, bool isSelecting, bool moveCaretPosition) { if (_presenter == null) { @@ -1727,10 +1727,13 @@ private void MoveHorizontal(int direction, bool wholeWord, bool isSelecting) } SetCurrentValue(SelectionEndProperty, SelectionEnd + offset); + + if (moveCaretPosition) + { + _presenter.MoveCaretToTextPosition(SelectionEnd); + } - _presenter.MoveCaretToTextPosition(SelectionEnd); - - if (!isSelecting) + if (!isSelecting && moveCaretPosition) { SetCurrentValue(CaretIndexProperty, SelectionEnd); } @@ -1867,7 +1870,7 @@ internal bool DeleteSelection() _presenter?.MoveCaretToTextPosition(start); - SetCurrentValue(CaretIndexProperty, start); + SetCurrentValue(SelectionStartProperty, start); ClearSelection(); @@ -1957,9 +1960,16 @@ private void RaiseTextChangeEvents() private void SetSelectionForControlBackspace() { + var text = Text ?? string.Empty; var selectionStart = CaretIndex; - MoveHorizontal(-1, true, false); + MoveHorizontal(-1, true, false, false); + + if (SelectionEnd > 0 && + selectionStart < text.Length && text[selectionStart] == ' ') + { + SetCurrentValue(SelectionEndProperty, SelectionEnd - 1); + } SetCurrentValue(SelectionStartProperty, selectionStart); } @@ -1974,7 +1984,7 @@ private void SetSelectionForControlDelete() SetCurrentValue(SelectionStartProperty, CaretIndex); - MoveHorizontal(1, true, true); + MoveHorizontal(1, true, true, false); if (SelectionEnd < textLength && Text![SelectionEnd] == ' ') { diff --git a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs index 1ee154c44a8..a53d1dd5a10 100644 --- a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs @@ -151,6 +151,79 @@ public void CaretIndex_Can_Moved_To_Position_After_The_End_Of_Text_With_Arrow_Ke Assert.Equal(4, target.CaretIndex); } } + + [Fact] + public void Control_Backspace_Should_Set_Caret_Position_To_The_Start_Of_The_Deletion() + { + using (UnitTestApplication.Start(Services)) + { + var target = new TextBox + { + Template = CreateTemplate(), + Text = "First Second Third", + SelectionStart = 13, + SelectionEnd = 13 + }; + + target.CaretIndex = 10; + target.ApplyTemplate(); + + // (First Second |Third) + RaiseKeyEvent(target, Key.Back, KeyModifiers.Control); + // (First |Third) + + Assert.Equal(6, target.CaretIndex); + } + } + + [Fact] + public void Control_Backspace_Should_Remove_The_Double_Whitespace_If_Caret_Index_Was_At_The_End_Of_A_Word() + { + using (UnitTestApplication.Start(Services)) + { + var target = new TextBox + { + Template = CreateTemplate(), + Text = "First Second Third", + SelectionStart = 12, + SelectionEnd = 12 + }; + + target.ApplyTemplate(); + + // (First Second| Third) + RaiseKeyEvent(target, Key.Back, KeyModifiers.Control); + // (First| Third) + + Assert.Equal("First Third", target.Text); + } + } + + [Fact] + public void Control_Backspace_Undo_Should_Return_Caret_Position() + { + using (UnitTestApplication.Start(Services)) + { + var target = new TextBox + { + Template = CreateTemplate(), + Text = "First Second Third", + SelectionStart = 9, + SelectionEnd = 9 + }; + + target.ApplyTemplate(); + + // (First Second| Third) + RaiseKeyEvent(target, Key.Back, KeyModifiers.Control); + // (First| Third) + + target.Undo(); + // (First Second| Third) + + Assert.Equal(9, target.CaretIndex); + } + } [Fact] public void Press_Ctrl_A_Select_All_Text()