Kaynağa Gözat

Fixes #3713 - `TextField` & `TextView` - When pressing right/left clear the selection instead of move tab (#3776)

* When pressing right clear the selection instead of move tab - TextField

* Add tests and start on TextView

* Finish tests for TextView

* Fix typos

* typo fix x2

* Fix for Selecting being renamed IsSelecting

---------

Co-authored-by: Tig <[email protected]>
Thomas Nind 9 ay önce
ebeveyn
işleme
cc0d9655e5

+ 20 - 20
Terminal.Gui/Views/TextField.cs

@@ -1519,18 +1519,28 @@ public class TextField : View
         }
     }
 
-    private bool MoveLeft ()
+    /// <summary>
+    /// Moves the cursor +/- the given <paramref name="distance"/>, clearing
+    /// any selection and returning true if any meaningful changes were made.
+    /// </summary>
+    /// <param name="distance">Distance to move the cursor, will be clamped to
+    /// text length. Positive for right, Negative for left.</param>
+    /// <returns></returns>
+    private bool Move (int distance)
     {
-        if (_cursorPosition > 0)
-        {
-            ClearAllSelection ();
-            _cursorPosition--;
-            Adjust ();
+        var oldCursorPosition = _cursorPosition;
+        var hadSelection = _selectedText != null && _selectedText.Length > 0;
 
-            return true;
-        }
+        _cursorPosition = Math.Min (_text.Count, Math.Max (0, _cursorPosition + distance));
+        ClearAllSelection ();
+        Adjust ();
 
-        return false;
+        return _cursorPosition != oldCursorPosition || hadSelection;
+    }
+
+    private bool MoveLeft ()
+    {
+        return Move (-1);
     }
 
     private void MoveLeftExtend ()
@@ -1543,17 +1553,7 @@ public class TextField : View
 
     private bool MoveRight ()
     {
-        if (_cursorPosition == _text.Count)
-        {
-            return false;
-        }
-
-        ClearAllSelection ();
-
-        _cursorPosition++;
-        Adjust ();
-
-        return true;
+        return Move (1);
     }
 
     private void MoveRightExtend ()

+ 12 - 0
Terminal.Gui/Views/TextView.cs

@@ -5958,6 +5958,11 @@ public class TextView : View
         // if the user presses Left (without any control keys) and they are at the start of the text
         if (CurrentColumn == 0 && CurrentRow == 0)
         {
+            if (IsSelecting)
+            {
+                StopSelecting ();
+                return true;
+            }
             // do not respond (this lets the key press fall through to navigation system - which usually changes focus backward)
             return false;
         }
@@ -5991,6 +5996,13 @@ public class TextView : View
         // if they are at the very end of all the text do not respond (this lets the key press fall through to navigation system - which usually changes focus forward)
         if (CurrentColumn == lastCol && CurrentRow == lastRow)
         {
+            // Unless they have text selected
+            if (IsSelecting)
+            {
+                // In which case clear
+                StopSelecting ();
+                return true;
+            }
             return false;
         }
 

+ 47 - 0
UnitTests/Views/TextFieldTests.cs

@@ -2089,6 +2089,53 @@ Les Miśerables",
         Assert.Equal (2, superView.Subviews.Count);
     }
 
+    [Fact]
+    public void Right_CursorAtEnd_WithSelection_ShouldClearSelection ()
+    {
+        var tf = new TextField
+        {
+            Text = "Hello",
+        };
+        tf.SetFocus ();
+        tf.SelectAll ();
+        tf.CursorPosition = 5;
+
+        // When there is selected text and the cursor is at the end of the text field
+        Assert.Equal ("Hello",tf.SelectedText);
+
+        // Pressing right should not move focus, instead it should clear selection
+        Assert.True(tf.NewKeyDownEvent (Key.CursorRight));
+        Assert.Null (tf.SelectedText);
+
+        // Now that the selection is cleared another right keypress should move focus
+        Assert.False (tf.NewKeyDownEvent (Key.CursorRight));
+    }
+    [Fact]
+    public void Left_CursorAtStart_WithSelection_ShouldClearSelection ()
+    {
+        var tf = new TextField
+        {
+            Text = "Hello",
+        };
+        tf.SetFocus ();
+
+        tf.CursorPosition = 2;
+        Assert.True (tf.NewKeyDownEvent (Key.CursorLeft.WithShift));
+        Assert.True (tf.NewKeyDownEvent (Key.CursorLeft.WithShift));
+
+        // When there is selected text and the cursor is at the start of the text field
+        Assert.Equal ("He", tf.SelectedText);
+
+        // Pressing left should not move focus, instead it should clear selection
+        Assert.True (tf.NewKeyDownEvent (Key.CursorLeft));
+        Assert.Null (tf.SelectedText);
+
+        // When clearing selected text with left the cursor should be at the start of the selection
+        Assert.Equal (0,tf.CursorPosition);
+
+        // Now that the selection is cleared another left keypress should move focus
+        Assert.False (tf.NewKeyDownEvent (Key.CursorLeft));
+    }
     [Fact]
     public void Autocomplete_Visible_False_By_Default ()
     {

+ 52 - 1
UnitTests/Views/TextViewTests.cs

@@ -8789,6 +8789,58 @@ line.
         Assert.False (t.Autocomplete.Visible);
     }
 
+    [Fact]
+    public void Right_CursorAtEnd_WithSelection_ShouldClearSelection ()
+    {
+        var tv = new TextView
+        {
+            Text = "Hello",
+        };
+        tv.SetFocus ();
+
+        tv.NewKeyDownEvent (Key.End.WithShift);
+        Assert.Equal (5,tv.CursorPosition.X);
+
+        // When there is selected text and the cursor is at the end of the text field
+        Assert.Equal ("Hello", tv.SelectedText);
+
+        // Pressing right should not move focus, instead it should clear selection
+        Assert.True (tv.NewKeyDownEvent (Key.CursorRight));
+        Assert.Empty (tv.SelectedText);
+
+        // Now that the selection is cleared another right keypress should move focus
+        Assert.False (tv.NewKeyDownEvent (Key.CursorRight));
+    }
+    [Fact]
+    public void Left_CursorAtStart_WithSelection_ShouldClearSelection ()
+    {
+        var tv = new TextView
+        {
+            Text = "Hello",
+        };
+        tv.SetFocus ();
+
+        tv.NewKeyDownEvent (Key.CursorRight);
+        tv.NewKeyDownEvent (Key.CursorRight);
+
+        Assert.Equal (2,tv.CursorPosition.X);
+
+        Assert.True (tv.NewKeyDownEvent (Key.CursorLeft.WithShift));
+        Assert.True (tv.NewKeyDownEvent (Key.CursorLeft.WithShift));
+
+        // When there is selected text and the cursor is at the start of the text field
+        Assert.Equal ("He", tv.SelectedText);
+
+        // Pressing left should not move focus, instead it should clear selection
+        Assert.True (tv.NewKeyDownEvent (Key.CursorLeft));
+        Assert.Empty (tv.SelectedText);
+
+        // When clearing selected text with left the cursor should be at the start of the selection
+        Assert.Equal (0, tv.CursorPosition.X);
+
+        // Now that the selection is cleared another left keypress should move focus
+        Assert.False (tv.NewKeyDownEvent (Key.CursorLeft));
+    }
     [Fact]
     [AutoInitShutdown]
     public void Draw_Esc_Rune ()
@@ -9047,5 +9099,4 @@ ror       ";
     }
 
     private TextView CreateTextView () { return new () { Width = 30, Height = 10 }; }
-
 }