瀏覽代碼

Fix: TextBox having selection and pressing up / down arrows (#18385)

Rastislav Svoboda 7 月之前
父節點
當前提交
6e1cab99f6
共有 2 個文件被更改,包括 198 次插入7 次删除
  1. 24 7
      src/Avalonia.Controls/TextBox.cs
  2. 174 0
      tests/Avalonia.Controls.UnitTests/TextBoxTests.cs

+ 24 - 7
src/Avalonia.Controls/TextBox.cs

@@ -1467,6 +1467,11 @@ namespace Avalonia.Controls
                         {
                             selection = DetectSelection();
 
+                            if (!selection && SelectionStart != SelectionEnd)
+                            {
+                                ClearSelectionAndMoveCaretToTextPosition(LogicalDirection.Backward);
+                            }
+
                             _presenter.MoveCaretVertical(LogicalDirection.Backward);
 
                             if (caretIndex != _presenter.CaretIndex)
@@ -1489,6 +1494,11 @@ namespace Avalonia.Controls
                         {
                             selection = DetectSelection();
 
+                            if (!selection && SelectionStart != SelectionEnd)
+                            {
+                                ClearSelectionAndMoveCaretToTextPosition(LogicalDirection.Forward);
+                            }
+
                             _presenter.MoveCaretVertical();
 
                             if (caretIndex != _presenter.CaretIndex)
@@ -1983,13 +1993,9 @@ namespace Avalonia.Controls
                 {
                     if (selectionStart != selectionEnd)
                     {
-                        // clear the selection and move to the appropriate side of previous selection
-                        var newPosition = direction > 0 ?
-                            Math.Max(selectionStart, selectionEnd) :
-                            Math.Min(selectionStart, selectionEnd);
-                        SetCurrentValue(SelectionStartProperty, newPosition);
-                        SetCurrentValue(SelectionEndProperty, newPosition);
-                        _presenter.MoveCaretToTextPosition(newPosition);
+                        ClearSelectionAndMoveCaretToTextPosition(direction > 0 ?
+                            LogicalDirection.Forward :
+                            LogicalDirection.Backward);
                     }
                     else
                     {
@@ -2100,6 +2106,17 @@ namespace Avalonia.Controls
             _scrollViewer?.PageDown();
         }
 
+        private void ClearSelectionAndMoveCaretToTextPosition(LogicalDirection direction)
+        {
+            var newPosition = direction == LogicalDirection.Forward ?
+                Math.Max(SelectionStart, SelectionEnd) :
+                Math.Min(SelectionStart, SelectionEnd);
+            SetCurrentValue(SelectionStartProperty, newPosition);
+            SetCurrentValue(SelectionEndProperty, newPosition);
+            // move caret to appropriate side of previous selection
+            _presenter?.MoveCaretToTextPosition(newPosition);
+        }
+
         /// <summary>
         /// Scroll the <see cref="TextBox"/> to the specified line index.
         /// </summary>

+ 174 - 0
tests/Avalonia.Controls.UnitTests/TextBoxTests.cs

@@ -1540,6 +1540,7 @@ namespace Avalonia.Controls.UnitTests
         [InlineData(0,4)]
         [InlineData(2,6)]
         [InlineData(0,6)]
+        [InlineData(3,4)]
         public void When_Selection_From_Left_To_Right_Pressing_Right_Should_Remove_Selection_Moving_Caret_To_End_Of_Previous_Selection(int selectionStart, int selectionEnd)
         {
             using (UnitTestApplication.Start(Services))
@@ -1568,6 +1569,7 @@ namespace Avalonia.Controls.UnitTests
         [InlineData(0,4)]
         [InlineData(2,6)]
         [InlineData(0,6)]
+        [InlineData(3,4)]
         public void When_Selection_From_Left_To_Right_Pressing_Left_Should_Remove_Selection_Moving_Caret_To_Start_Of_Previous_Selection(int selectionStart, int selectionEnd)
         {
             using (UnitTestApplication.Start(Services))
@@ -1596,6 +1598,7 @@ namespace Avalonia.Controls.UnitTests
         [InlineData(4,0)]
         [InlineData(6,2)]
         [InlineData(6,0)]
+        [InlineData(4,3)]
         public void When_Selection_From_Right_To_Left_Pressing_Right_Should_Remove_Selection_Moving_Caret_To_Start_Of_Previous_Selection(int selectionStart, int selectionEnd)
         {
             using (UnitTestApplication.Start(Services))
@@ -1624,6 +1627,7 @@ namespace Avalonia.Controls.UnitTests
         [InlineData(4,0)]
         [InlineData(6,2)]
         [InlineData(6,0)]
+        [InlineData(4,3)]
         public void When_Selection_From_Right_To_Left_Pressing_Left_Should_Remove_Selection_Moving_Caret_To_End_Of_Previous_Selection(int selectionStart, int selectionEnd)
         {
             using (UnitTestApplication.Start(Services))
@@ -1701,6 +1705,176 @@ namespace Avalonia.Controls.UnitTests
             }
         }
 
+        [Theory]
+        [InlineData(2,4)]
+        [InlineData(0,4)]
+        [InlineData(2,6)]
+        [InlineData(0,6)]
+        [InlineData(3,4)]
+        public void When_Selection_From_Left_To_Right_Pressing_Up_Should_Remove_Selection_Moving_Caret_To_Start_Of_Previous_Selection(int selectionStart, int selectionEnd)
+        {
+            using (UnitTestApplication.Start(Services))
+            {
+                var tb = new TextBox
+                {
+                    Template = CreateTemplate(),
+                    Text = "ABCDEF"
+                };
+
+                tb.Measure(Size.Infinity);
+                tb.CaretIndex = selectionStart;
+                tb.SelectionStart = selectionStart;
+                tb.SelectionEnd = selectionEnd;
+
+                RaiseKeyEvent(tb, Key.Up, KeyModifiers.None);
+
+                Assert.Equal(selectionStart, tb.SelectionStart);
+                Assert.Equal(selectionStart, tb.SelectionEnd);
+                Assert.Equal(selectionStart, tb.CaretIndex);
+            }
+        }
+
+        [Theory]
+        [InlineData(4,2)]
+        [InlineData(4,0)]
+        [InlineData(6,2)]
+        [InlineData(6,0)]
+        [InlineData(4,3)]
+        public void When_Selection_From_Right_To_Left_Pressing_Up_Should_Remove_Selection_Moving_Caret_To_End_Of_Previous_Selection(int selectionStart, int selectionEnd)
+        {
+            using (UnitTestApplication.Start(Services))
+            {
+                var tb = new TextBox
+                {
+                    Template = CreateTemplate(),
+                    Text = "ABCDEF"
+                };
+
+                tb.Measure(Size.Infinity);
+                tb.CaretIndex = selectionStart;
+                tb.SelectionStart = selectionStart;
+                tb.SelectionEnd = selectionEnd;
+
+                RaiseKeyEvent(tb, Key.Up, KeyModifiers.None);
+
+                Assert.Equal(selectionEnd, tb.SelectionStart);
+                Assert.Equal(selectionEnd, tb.SelectionEnd);
+                Assert.Equal(selectionEnd, tb.CaretIndex);
+            }
+        }
+
+        [Theory]
+        [InlineData(0)]
+        [InlineData(2)]
+        [InlineData(4)]
+        [InlineData(6)]
+        public void When_Select_All_From_Position_Up_Should_Remove_Selection_Moving_Caret_To_Start(int caretIndex)
+        {
+            using (UnitTestApplication.Start(Services))
+            {
+                var tb = new TextBox
+                {
+                    Template = CreateTemplate(),
+                    Text = "ABCDEF"
+                };
+
+                tb.Measure(Size.Infinity);
+                tb.CaretIndex = caretIndex;
+
+                RaiseKeyEvent(tb, Key.A, KeyModifiers.Control);
+                RaiseKeyEvent(tb, Key.Up, KeyModifiers.None);
+
+                Assert.Equal(0, tb.SelectionStart);
+                Assert.Equal(0, tb.SelectionEnd);
+                Assert.Equal(0, tb.CaretIndex);
+            }
+        }
+
+        [Theory]
+        [InlineData(2,4)]
+        [InlineData(0,4)]
+        [InlineData(2,6)]
+        [InlineData(0,6)]
+        [InlineData(3,4)]
+        public void When_Selection_From_Left_To_Right_Pressing_Down_Should_Remove_Selection_Moving_Caret_To_End_Of_Previous_Selection(int selectionStart, int selectionEnd)
+        {
+            using (UnitTestApplication.Start(Services))
+            {
+                var tb = new TextBox
+                {
+                    Template = CreateTemplate(),
+                    Text = "ABCDEF"
+                };
+
+                tb.Measure(Size.Infinity);
+                tb.CaretIndex = selectionStart;
+                tb.SelectionStart = selectionStart;
+                tb.SelectionEnd = selectionEnd;
+
+                RaiseKeyEvent(tb, Key.Down, KeyModifiers.None);
+
+                Assert.Equal(selectionEnd, tb.SelectionStart);
+                Assert.Equal(selectionEnd, tb.SelectionEnd);
+                Assert.Equal(selectionEnd, tb.CaretIndex);
+            }
+        }
+
+        [Theory]
+        [InlineData(4,2)]
+        [InlineData(4,0)]
+        [InlineData(6,2)]
+        [InlineData(6,0)]
+        [InlineData(4,3)]
+        public void When_Selection_From_Right_To_Left_Pressing_Down_Should_Remove_Selection_Moving_Caret_To_Start_Of_Previous_Selection(int selectionStart, int selectionEnd)
+        {
+            using (UnitTestApplication.Start(Services))
+            {
+                var tb = new TextBox
+                {
+                    Template = CreateTemplate(),
+                    Text = "ABCDEF"
+                };
+
+                tb.Measure(Size.Infinity);
+                tb.CaretIndex = selectionStart;
+                tb.SelectionStart = selectionStart;
+                tb.SelectionEnd = selectionEnd;
+
+                RaiseKeyEvent(tb, Key.Down, KeyModifiers.None);
+
+                Assert.Equal(selectionStart, tb.SelectionStart);
+                Assert.Equal(selectionStart, tb.SelectionEnd);
+                Assert.Equal(selectionStart, tb.CaretIndex);
+            }
+        }
+
+        [Theory]
+        [InlineData(0)]
+        [InlineData(2)]
+        [InlineData(4)]
+        [InlineData(6)]
+        public void When_Select_All_From_Position_Down_Should_Remove_Selection_Moving_Caret_To_End(int caretIndex)
+        {
+            using (UnitTestApplication.Start(Services))
+            {
+                var tb = new TextBox
+                {
+                    Template = CreateTemplate(),
+                    Text = "ABCDEF"
+                };
+
+                tb.Measure(Size.Infinity);
+                tb.CaretIndex = caretIndex;
+
+                RaiseKeyEvent(tb, Key.A, KeyModifiers.Control);
+                RaiseKeyEvent(tb, Key.Down, KeyModifiers.None);
+
+                Assert.Equal(tb.Text.Length, tb.SelectionStart);
+                Assert.Equal(tb.Text.Length, tb.SelectionEnd);
+                Assert.Equal(tb.Text.Length, tb.CaretIndex);
+            }
+        }
+
         [Fact]
         public void TextBox_In_AdornerLayer_Will_Not_Cause_Collection_Modified_In_VisualLayerManager_Measure()
         {