diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index 7d9785c22c..c9d6818672 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -1519,18 +1519,28 @@ private void MoveHomeExtend () } } - private bool MoveLeft () + /// + /// Moves the cursor +/- the given , clearing + /// any selection and returning true if any meaningful changes were made. + /// + /// Distance to move the cursor, will be clamped to + /// text length. Positive for right, Negative for left. + /// + 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 @@ private void MoveLeftExtend () private bool MoveRight () { - if (_cursorPosition == _text.Count) - { - return false; - } - - ClearAllSelection (); - - _cursorPosition++; - Adjust (); - - return true; + return Move (1); } private void MoveRightExtend () diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index c34e9220bd..674d8328ed 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -5958,6 +5958,11 @@ private bool ProcessMoveLeft () // 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 @@ private bool ProcessMoveRight () // 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; } diff --git a/UnitTests/Views/TextFieldTests.cs b/UnitTests/Views/TextFieldTests.cs index 4e1fec138b..d2f8ba4c5a 100644 --- a/UnitTests/Views/TextFieldTests.cs +++ b/UnitTests/Views/TextFieldTests.cs @@ -2089,6 +2089,53 @@ public void Autocomplete__Added_To_SuperView_On_Add () 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 () { diff --git a/UnitTests/Views/TextViewTests.cs b/UnitTests/Views/TextViewTests.cs index a05af9ef07..a912064f6d 100644 --- a/UnitTests/Views/TextViewTests.cs +++ b/UnitTests/Views/TextViewTests.cs @@ -8789,6 +8789,58 @@ public void Autocomplete_Visible_False_By_Default () 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 @@ public void Cell_LoadCells_Without_ColorScheme_Is_Never_Null () } private TextView CreateTextView () { return new () { Width = 30, Height = 10 }; } - }