Browse Source

Fixes #4096. Ctrl+Delete/Backspace (#4099)

* Fixes #4096. Ctrl+Delete/Backspace

* Fixes Ctrl+Backspace exception

* Fixes #4039. Keyboard-selected text not hightlighted in TextView

* Fixes #4048. Selecting text with mouse isn't working in v2win driver

* Fixes #4061. Last character of line not mouse-selectable in TextView

* Fix unit test not updating cursor position by not invoke UnwrappedCursorPosition

* Reinforcing unit test

* Fix v2win input processor

* Fixes #4100. PopupAutoComplete isn't clearing after pressing ESC

* Cleanup code

* Add ControlKeyState into unit tests

* Fix merge conflicts
BDisp 3 months ago
parent
commit
5b85b16b61

+ 10 - 0
Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs

@@ -777,6 +777,10 @@ internal class CursesDriver : ConsoleDriver
                 wch -= 60;
                 k = KeyCode.ShiftMask | KeyCode.AltMask | MapCursesKey (wch);
             }
+            else if (wch == 520) // Ctrl+Delete
+            {
+                k = KeyCode.CtrlMask | KeyCode.Delete;
+            }
 
             OnKeyDown (new Key (k));
             OnKeyUp (new Key (k));
@@ -878,6 +882,12 @@ internal class CursesDriver : ConsoleDriver
             OnKeyDown (key);
             OnKeyUp (key);
         }
+        else if (wch == 8) // Ctrl+Backspace
+        {
+            k = KeyCode.Backspace | KeyCode.CtrlMask;
+            OnKeyDown (new Key (k));
+            OnKeyUp (new Key (k));
+        }
         else if (wch == Curses.KeyTab)
         {
             k = MapCursesKey (wch);

+ 11 - 0
Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs

@@ -1123,6 +1123,17 @@ public static class EscSeqUtils
                                              (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0,
                                              true);
                 }
+                else if (consoleKeyInfo is { Key: 0, KeyChar: '\b' })
+                {
+                    key = ConsoleKey.Backspace;
+
+                    newConsoleKeyInfo = new (
+                                             consoleKeyInfo.KeyChar,
+                                             key,
+                                             (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0,
+                                             (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0,
+                                             true);
+                }
                 else if (consoleKeyInfo.Key == 0)
                 {
                     key = (ConsoleKey)(char)(consoleKeyInfo.KeyChar + (uint)ConsoleKey.A - 1);

+ 33 - 8
Terminal.Gui/ConsoleDrivers/V2/WindowsInputProcessor.cs

@@ -81,7 +81,7 @@ internal class WindowsInputProcessor : InputProcessor<InputRecord>
 
     public MouseEventArgs ToDriverMouse (MouseEventRecord e)
     {
-        var mouseFlags = MouseFlags.ReportMousePosition;
+        var mouseFlags = MouseFlags.None;
 
         mouseFlags = UpdateMouseFlags (mouseFlags, e.ButtonState, ButtonState.Button1Pressed, MouseFlags.Button1Pressed, MouseFlags.Button1Released, 0);
         mouseFlags = UpdateMouseFlags (mouseFlags, e.ButtonState, ButtonState.Button2Pressed, MouseFlags.Button2Pressed, MouseFlags.Button2Released, 1);
@@ -100,9 +100,6 @@ internal class WindowsInputProcessor : InputProcessor<InputRecord>
             {
                 mouseFlags |= MouseFlags.Button3Released;
 
-                // Removes the moved flag when raising released flags (see https://github.com/gui-cs/Terminal.Gui/issues/4088)
-                mouseFlags &= ~MouseFlags.ReportMousePosition;
-
                 _lastWasPressed [2] = false;
             }
         }
@@ -123,14 +120,44 @@ internal class WindowsInputProcessor : InputProcessor<InputRecord>
             }
         }
 
+        if (e.EventFlags != EventFlags.NoEvent)
+        {
+            switch (e.EventFlags)
+            {
+                case EventFlags.MouseMoved:
+                    mouseFlags |= MouseFlags.ReportMousePosition;
+
+                    break;
+            }
+        }
+
+        if (e.ControlKeyState != ControlKeyState.NoControlKeyPressed)
+        {
+            switch (e.ControlKeyState)
+            {
+                case ControlKeyState.RightAltPressed:
+                case ControlKeyState.LeftAltPressed:
+                    mouseFlags |= MouseFlags.ButtonAlt;
+
+                    break;
+                case ControlKeyState.RightControlPressed:
+                case ControlKeyState.LeftControlPressed:
+                    mouseFlags |= MouseFlags.ButtonCtrl;
+
+                    break;
+                case ControlKeyState.ShiftPressed:
+                    mouseFlags |= MouseFlags.ButtonShift;
+
+                    break;
+            }
+        }
+
         var result = new MouseEventArgs
         {
             Position = new (e.MousePosition.X, e.MousePosition.Y),
             Flags = mouseFlags
         };
 
-        // TODO: Return keys too
-
         return result;
     }
 
@@ -154,8 +181,6 @@ internal class WindowsInputProcessor : InputProcessor<InputRecord>
             {
                 current |= releasedFlag;
 
-                // Removes the moved flag when raising released flags (see https://github.com/gui-cs/Terminal.Gui/issues/4088)
-                current &= ~MouseFlags.ReportMousePosition;
                 _lastWasPressed [buttonIndex] = false;
             }
         }

+ 9 - 2
Terminal.Gui/ConsoleDrivers/WindowsDriver/WindowsDriver.cs

@@ -789,11 +789,11 @@ internal class WindowsDriver : ConsoleDriver
                     if (keyInfo.KeyChar <= 'Z')
                     {
                         return (KeyCode)keyInfo.Key | KeyCode.ShiftMask;
-                }
+                    }
 
                     // Always return the KeyChar because it may be an Á, À with Oem1, etc
                     return (KeyCode)keyInfo.KeyChar;
-            }
+                }
             }
 
             if (keyInfo.KeyChar <= 'z')
@@ -806,6 +806,7 @@ internal class WindowsDriver : ConsoleDriver
         }
 
         // Handle control keys whose VK codes match the related ASCII value (those below ASCII 33) like ESC
+        // Also handle the key ASCII value 127 (BACK)
         if (Enum.IsDefined (typeof (KeyCode), (uint)keyInfo.Key))
         {
             // If the key is JUST a modifier, return it as just that key
@@ -829,6 +830,12 @@ internal class WindowsDriver : ConsoleDriver
                 return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)keyInfo.KeyChar);
             }
 
+            // Backspace (ASCII 127)
+            if (keyInfo.KeyChar == '\u007f')
+            {
+                return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)keyInfo.Key);
+            }
+
             if (keyInfo.Key != ConsoleKey.None)
             {
                 return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)keyInfo.KeyChar);

+ 1 - 1
Terminal.Gui/Text/Autocomplete/AutocompleteBase.cs

@@ -43,7 +43,7 @@ public abstract class AutocompleteBase : IAutocomplete
     public virtual Key CloseKey { get; set; } = Key.Esc;
 
     /// <inheritdoc/>
-    public virtual Key Reopen { get; set; } = Key.Space.WithCtrl.WithAlt;
+    public virtual Key Reopen { get; set; } = Key.Space.WithShift;
 
     /// <inheritdoc/>
     public virtual AutocompleteContext Context { get; set; }

+ 4 - 2
Terminal.Gui/Text/Autocomplete/PopupAutocomplete.cs

@@ -285,7 +285,7 @@ public abstract partial class PopupAutocomplete : AutocompleteBase
         if (PopupInsideContainer)
         {
             // don't overspill vertically
-            height = Math.Min (HostControl.Viewport.Height - renderAt.Y, MaxHeight);
+            height = Math.Min (Math.Min (HostControl!.Viewport.Height - renderAt.Y, MaxHeight), Suggestions.Count);
 
             // There is no space below, lets see if can popup on top
             if (height < Suggestions.Count && HostControl.Viewport.Height - renderAt.Y >= height)
@@ -419,8 +419,9 @@ public abstract partial class PopupAutocomplete : AutocompleteBase
         ClearSuggestions ();
         Visible = false;
         _closed = true;
-        HostControl?.SetNeedsDraw ();
         //RemovePopupFromTop ();
+        _popup.Visible = false;
+        HostControl?.SetNeedsDraw ();
     }
 
     /// <summary>Deletes the text backwards before insert the selected text in the <see cref="HostControl"/>.</summary>
@@ -507,6 +508,7 @@ public abstract partial class PopupAutocomplete : AutocompleteBase
         {
             Visible = true;
             _closed = false;
+            _popup.Visible = true;
             HostControl?.SetNeedsDraw ();
 
             return true;

+ 2 - 1
Terminal.Gui/Views/TextInput/TextField.cs

@@ -1015,7 +1015,6 @@ public class TextField : View, IDesignable
 
         RenderCaption ();
 
-        DrawAutocomplete ();
         _isDrawing = false;
 
         return true;
@@ -1686,6 +1685,8 @@ public class TextField : View, IDesignable
         }
 
         GenerateSuggestions ();
+
+        DrawAutocomplete ();
     }
 
     private void DrawAutocomplete ()

+ 3 - 1
Terminal.Gui/Views/TextInput/TextModel.cs

@@ -269,9 +269,11 @@ internal class TextModel
 
                     if (nRow != fromRow && (Rune.IsLetterOrDigit (nRune) || Rune.IsPunctuation (nRune) || Rune.IsSymbol (nRune)))
                     {
+                        List<Cell> line = GetLine (nRow);
+
                         if (lastValidCol > -1)
                         {
-                            nCol = lastValidCol;
+                            nCol = lastValidCol + Math.Max (lastValidCol, line.Count);
                         }
 
                         return;

+ 37 - 16
Terminal.Gui/Views/TextInput/TextView.cs

@@ -1715,6 +1715,7 @@ public class TextView : View, IDesignable
             PositionCursor ();
             _lastWasKill = false;
             _columnTrack = CurrentColumn;
+            SetNeedsDraw ();
         }
         else if (ev.Flags.HasFlag (MouseFlags.Button1TripleClicked))
         {
@@ -1735,6 +1736,7 @@ public class TextView : View, IDesignable
             PositionCursor ();
             _lastWasKill = false;
             _columnTrack = CurrentColumn;
+            SetNeedsDraw ();
         }
         else if (ev.Flags == ContextMenu!.MouseFlags)
         {
@@ -1747,6 +1749,8 @@ public class TextView : View, IDesignable
             //ShowContextMenu ();
         }
 
+        OnUnwrappedCursorPosition ();
+
         return true;
     }
 
@@ -1757,7 +1761,7 @@ public class TextView : View, IDesignable
         List<Cell> line = GetCurrentLine ();
         CurrentColumn = line.Count;
         TrackColumn ();
-        PositionCursor ();
+        DoNeededAction ();
     }
 
     /// <summary>Will scroll the <see cref="TextView"/> to the first line and position the cursor there.</summary>
@@ -1768,8 +1772,7 @@ public class TextView : View, IDesignable
         CurrentColumn = 0;
         _leftColumn = 0;
         TrackColumn ();
-        PositionCursor ();
-        SetNeedsDraw ();
+        DoNeededAction ();
     }
 
     /// <summary>
@@ -2661,6 +2664,11 @@ public class TextView : View, IDesignable
 
     private void DoNeededAction ()
     {
+        if (!NeedsDraw && (IsSelecting || _wrapNeeded || !Used))
+        {
+            SetNeedsDraw ();
+        }
+
         if (NeedsDraw)
         {
             Adjust ();
@@ -2668,6 +2676,7 @@ public class TextView : View, IDesignable
         else
         {
             PositionCursor ();
+            OnUnwrappedCursorPosition ();
         }
     }
 
@@ -3385,8 +3394,24 @@ public class TextView : View, IDesignable
         }
         else if (newPos.HasValue)
         {
-            int restCount = currentLine.Count - CurrentColumn;
-            currentLine.RemoveRange (CurrentColumn, restCount);
+            int restCount;
+
+            if (newPos.Value.row == CurrentRow)
+            {
+                restCount = currentLine.Count - CurrentColumn;
+                currentLine.RemoveRange (CurrentColumn, restCount);
+            }
+            else
+            {
+                while (CurrentRow != newPos.Value.row)
+                {
+                    restCount = currentLine.Count;
+                    currentLine.RemoveRange (0, restCount);
+
+                    CurrentRow--;
+                    currentLine = GetCurrentLine ();
+                }
+            }
 
             if (_wordWrap)
             {
@@ -3540,8 +3565,7 @@ public class TextView : View, IDesignable
     private void MoveEndOfLine ()
     {
         List<Cell> currentLine = GetCurrentLine ();
-        CurrentColumn = Math.Max (currentLine.Count - (ReadOnly ? 1 : 0), 0);
-        Adjust ();
+        CurrentColumn = currentLine.Count;
         DoNeededAction ();
     }
 
@@ -3572,7 +3596,6 @@ public class TextView : View, IDesignable
             }
         }
 
-        Adjust ();
         DoNeededAction ();
 
         return true;
@@ -3654,10 +3677,6 @@ public class TextView : View, IDesignable
                     _topRow++;
                     SetNeedsDraw ();
                 }
-                else
-                {
-                    return false;
-                }
             }
             else
             {
@@ -3665,7 +3684,6 @@ public class TextView : View, IDesignable
             }
         }
 
-        Adjust ();
         DoNeededAction ();
 
         return true;
@@ -3680,7 +3698,6 @@ public class TextView : View, IDesignable
 
         CurrentColumn = 0;
         _leftColumn = 0;
-        Adjust ();
         DoNeededAction ();
     }
 
@@ -3743,7 +3760,6 @@ public class TextView : View, IDesignable
             CurrentRow = newPos.Value.row;
         }
 
-        Adjust ();
         DoNeededAction ();
     }
 
@@ -3757,7 +3773,6 @@ public class TextView : View, IDesignable
             CurrentRow = newPos.Value.row;
         }
 
-        Adjust ();
         DoNeededAction ();
     }
 
@@ -4031,6 +4046,7 @@ public class TextView : View, IDesignable
     private void ProcessKillWordForward ()
     {
         ResetColumnTrack ();
+        StopSelecting ();
         KillWordForward ();
     }
 
@@ -4578,6 +4594,11 @@ public class TextView : View, IDesignable
 
     private void StopSelecting ()
     {
+        if (IsSelecting)
+        {
+            SetNeedsDraw ();
+        }
+
         _shiftSelecting = false;
         IsSelecting = false;
         _isButtonShift = false;

+ 132 - 37
Tests/UnitTests/ConsoleDrivers/V2/WindowsInputProcessorTests.cs

@@ -2,6 +2,8 @@
 using Terminal.Gui.ConsoleDrivers;
 using InputRecord = Terminal.Gui.WindowsConsole.InputRecord;
 using ButtonState = Terminal.Gui.WindowsConsole.ButtonState;
+using EventFlags = Terminal.Gui.WindowsConsole.EventFlags;
+using ControlKeyState = Terminal.Gui.WindowsConsole.ControlKeyState;
 using MouseEventRecord = Terminal.Gui.WindowsConsole.MouseEventRecord;
 
 namespace UnitTests.ConsoleDrivers.V2;
@@ -102,7 +104,7 @@ public class WindowsInputProcessorTests
                                MousePosition = new (32, 31),
                                ButtonState = ButtonState.NoButtonPressed,
                                ControlKeyState = WindowsConsole.ControlKeyState.NoControlKeyPressed,
-                               EventFlags = WindowsConsole.EventFlags.MouseMoved
+                               EventFlags = EventFlags.MouseMoved
                            }
                        });
 
@@ -139,7 +141,7 @@ public class WindowsInputProcessorTests
                                MousePosition = new (32, 31),
                                ButtonState = state,
                                ControlKeyState = WindowsConsole.ControlKeyState.NoControlKeyPressed,
-                               EventFlags = WindowsConsole.EventFlags.MouseMoved
+                               EventFlags = EventFlags.MouseMoved
                            }
                        });
 
@@ -199,9 +201,9 @@ public class WindowsInputProcessorTests
         {
             new []
             {
-                Tuple.Create (ButtonState.Button1Pressed, MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition),
-                Tuple.Create (ButtonState.NoButtonPressed, MouseFlags.Button1Released),
-                Tuple.Create (ButtonState.NoButtonPressed, MouseFlags.ReportMousePosition | MouseFlags.ReportMousePosition)
+                Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button1Pressed),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button1Released),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.None)
             }
         };
 
@@ -209,9 +211,9 @@ public class WindowsInputProcessorTests
         {
             new []
             {
-                Tuple.Create (ButtonState.Button2Pressed, MouseFlags.Button2Pressed | MouseFlags.ReportMousePosition),
-                Tuple.Create (ButtonState.NoButtonPressed, MouseFlags.Button2Released),
-                Tuple.Create (ButtonState.NoButtonPressed, MouseFlags.ReportMousePosition | MouseFlags.ReportMousePosition)
+                Tuple.Create (ButtonState.Button2Pressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed, MouseFlags.Button2Pressed | MouseFlags.ReportMousePosition),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button2Released),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.None)
             }
         };
 
@@ -219,9 +221,9 @@ public class WindowsInputProcessorTests
         {
             new []
             {
-                Tuple.Create (ButtonState.Button3Pressed, MouseFlags.Button3Pressed | MouseFlags.ReportMousePosition),
-                Tuple.Create (ButtonState.NoButtonPressed, MouseFlags.Button3Released),
-                Tuple.Create (ButtonState.NoButtonPressed, MouseFlags.ReportMousePosition | MouseFlags.ReportMousePosition)
+                Tuple.Create (ButtonState.Button3Pressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Pressed | MouseFlags.ReportMousePosition),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Released),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.None)
             }
         };
 
@@ -229,9 +231,9 @@ public class WindowsInputProcessorTests
         {
             new []
             {
-                Tuple.Create (ButtonState.Button4Pressed, MouseFlags.Button4Pressed | MouseFlags.ReportMousePosition),
-                Tuple.Create (ButtonState.NoButtonPressed, MouseFlags.Button4Released),
-                Tuple.Create (ButtonState.NoButtonPressed, MouseFlags.ReportMousePosition | MouseFlags.ReportMousePosition)
+                Tuple.Create (ButtonState.Button4Pressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed, MouseFlags.Button4Pressed | MouseFlags.ReportMousePosition),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button4Released),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed, MouseFlags.ReportMousePosition)
             }
         };
 
@@ -239,9 +241,9 @@ public class WindowsInputProcessorTests
         {
             new []
             {
-                Tuple.Create (ButtonState.RightmostButtonPressed, MouseFlags.Button3Pressed | MouseFlags.ReportMousePosition),
-                Tuple.Create (ButtonState.NoButtonPressed, MouseFlags.Button3Released),
-                Tuple.Create (ButtonState.NoButtonPressed, MouseFlags.ReportMousePosition | MouseFlags.ReportMousePosition)
+                Tuple.Create (ButtonState.RightmostButtonPressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Pressed | MouseFlags.ReportMousePosition),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Released),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.None)
             }
         };
 
@@ -251,11 +253,11 @@ public class WindowsInputProcessorTests
             new []
             {
                 Tuple.Create (
-                              ButtonState.Button1Pressed | ButtonState.Button2Pressed,
+                              ButtonState.Button1Pressed | ButtonState.Button2Pressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed,
                               MouseFlags.Button1Pressed | MouseFlags.Button2Pressed | MouseFlags.ReportMousePosition),
-                Tuple.Create (ButtonState.Button1Pressed, MouseFlags.Button1Pressed | MouseFlags.Button2Released),
-                Tuple.Create (ButtonState.NoButtonPressed, MouseFlags.Button1Released),
-                Tuple.Create (ButtonState.NoButtonPressed, MouseFlags.ReportMousePosition)
+                Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button1Pressed | MouseFlags.Button2Released),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button1Released),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.None)
             }
         };
 
@@ -264,11 +266,11 @@ public class WindowsInputProcessorTests
             new []
             {
                 Tuple.Create (
-                              ButtonState.Button3Pressed | ButtonState.Button4Pressed,
+                              ButtonState.Button3Pressed | ButtonState.Button4Pressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed,
                               MouseFlags.Button3Pressed | MouseFlags.Button4Pressed | MouseFlags.ReportMousePosition),
-                Tuple.Create (ButtonState.Button3Pressed, MouseFlags.Button3Pressed | MouseFlags.Button4Released),
-                Tuple.Create (ButtonState.NoButtonPressed, MouseFlags.Button3Released),
-                Tuple.Create (ButtonState.NoButtonPressed, MouseFlags.ReportMousePosition)
+                Tuple.Create (ButtonState.Button3Pressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Pressed | MouseFlags.Button4Released),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Released),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.None)
             }
         };
 
@@ -278,10 +280,10 @@ public class WindowsInputProcessorTests
             new []
             {
                 Tuple.Create (
-                              ButtonState.Button1Pressed | ButtonState.Button2Pressed,
+                              ButtonState.Button1Pressed | ButtonState.Button2Pressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed,
                               MouseFlags.Button1Pressed | MouseFlags.Button2Pressed | MouseFlags.ReportMousePosition),
-                Tuple.Create (ButtonState.NoButtonPressed, MouseFlags.Button1Released | MouseFlags.Button2Released),
-                Tuple.Create (ButtonState.NoButtonPressed, MouseFlags.ReportMousePosition)
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button1Released | MouseFlags.Button2Released),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.None)
             }
         };
 
@@ -290,31 +292,124 @@ public class WindowsInputProcessorTests
         {
             new []
             {
-                Tuple.Create (ButtonState.Button3Pressed | ButtonState.RightmostButtonPressed, MouseFlags.Button3Pressed | MouseFlags.ReportMousePosition),
+                Tuple.Create (ButtonState.Button3Pressed | ButtonState.RightmostButtonPressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed,
+                              MouseFlags.Button3Pressed | MouseFlags.ReportMousePosition),
 
                 // Can swap between without raising the released
-                Tuple.Create (ButtonState.Button3Pressed, MouseFlags.Button3Pressed | MouseFlags.ReportMousePosition),
-                Tuple.Create (ButtonState.RightmostButtonPressed, MouseFlags.Button3Pressed | MouseFlags.ReportMousePosition),
+                Tuple.Create (ButtonState.Button3Pressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Pressed | MouseFlags.ReportMousePosition),
+                Tuple.Create (ButtonState.RightmostButtonPressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Pressed | MouseFlags.ReportMousePosition),
 
                 // Now with neither we get released
-                Tuple.Create (ButtonState.NoButtonPressed, MouseFlags.Button3Released),
-                Tuple.Create (ButtonState.NoButtonPressed, MouseFlags.ReportMousePosition)
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Released),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.None)
+            }
+        };
+
+        // Test for ControlKeyState buttons pressed and handled
+        yield return new object []
+        {
+            new []
+            {
+                Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.LeftAltPressed, MouseFlags.Button1Pressed | MouseFlags.ButtonAlt),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.LeftAltPressed, MouseFlags.Button1Released | MouseFlags.ButtonAlt),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.LeftAltPressed, MouseFlags.None | MouseFlags.ButtonAlt)
+            }
+        };
+
+        yield return new object []
+        {
+            new []
+            {
+                Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.RightAltPressed, MouseFlags.Button1Pressed | MouseFlags.ButtonAlt),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.RightAltPressed, MouseFlags.Button1Released | MouseFlags.ButtonAlt),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.RightAltPressed, MouseFlags.None | MouseFlags.ButtonAlt)
+            }
+        };
+
+        yield return new object []
+        {
+            new []
+            {
+                Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.LeftControlPressed, MouseFlags.Button1Pressed | MouseFlags.ButtonCtrl),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.LeftControlPressed, MouseFlags.Button1Released | MouseFlags.ButtonCtrl),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.LeftControlPressed, MouseFlags.None | MouseFlags.ButtonCtrl)
+            }
+        };
+
+        yield return new object []
+        {
+            new []
+            {
+                Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.RightControlPressed, MouseFlags.Button1Pressed | MouseFlags.ButtonCtrl),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.RightControlPressed, MouseFlags.Button1Released | MouseFlags.ButtonCtrl),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.RightControlPressed, MouseFlags.None | MouseFlags.ButtonCtrl)
+            }
+        };
+
+        yield return new object []
+        {
+            new []
+            {
+                Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.ShiftPressed, MouseFlags.Button1Pressed | MouseFlags.ButtonShift),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.ShiftPressed, MouseFlags.Button1Released | MouseFlags.ButtonShift),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.ShiftPressed, MouseFlags.None | MouseFlags.ButtonShift)
+            }
+        };
+
+        // Test for ControlKeyState buttons pressed and not handled
+        yield return new object []
+        {
+            new []
+            {
+                Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.CapslockOn, MouseFlags.Button1Pressed),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.CapslockOn, MouseFlags.Button1Released),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.CapslockOn, MouseFlags.None)
+            }
+        };
+
+        yield return new object []
+        {
+            new []
+            {
+                Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.EnhancedKey, MouseFlags.Button1Pressed),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.EnhancedKey, MouseFlags.Button1Released),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.EnhancedKey, MouseFlags.None)
+            }
+        };
+
+        yield return new object []
+        {
+            new []
+            {
+                Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.NumlockOn, MouseFlags.Button1Pressed),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NumlockOn, MouseFlags.Button1Released),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NumlockOn, MouseFlags.None)
+            }
+        };
+
+        yield return new object []
+        {
+            new []
+            {
+                Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.ScrolllockOn, MouseFlags.Button1Pressed),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.ScrolllockOn, MouseFlags.Button1Released),
+                Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.ScrolllockOn, MouseFlags.None)
             }
         };
     }
 
     [Theory]
     [MemberData (nameof (MouseFlagTestData))]
-    internal void MouseFlags_Should_Map_Correctly (Tuple<ButtonState, MouseFlags> [] inputOutputPairs)
+    internal void MouseFlags_Should_Map_Correctly (Tuple<ButtonState, EventFlags, ControlKeyState, MouseFlags> [] inputOutputPairs)
     {
         var processor = new WindowsInputProcessor (new ());
 
-        foreach (Tuple<ButtonState, MouseFlags> pair in inputOutputPairs)
+        foreach (Tuple<ButtonState, EventFlags, ControlKeyState, MouseFlags> pair in inputOutputPairs)
         {
-            var mockEvent = new MouseEventRecord { ButtonState = pair.Item1 };
+            var mockEvent = new MouseEventRecord { ButtonState = pair.Item1, EventFlags = pair.Item2, ControlKeyState = pair.Item3};
             MouseEventArgs result = processor.ToDriverMouse (mockEvent);
 
-            Assert.Equal (pair.Item2, result.Flags);
+            Assert.Equal (pair.Item4, result.Flags);
         }
     }
 }

+ 20 - 7
Tests/UnitTests/Views/AppendAutocompleteTests.cs

@@ -10,10 +10,12 @@ public class AppendAutocompleteTests (ITestOutputHelper output)
     public void TestAutoAppend_AfterCloseKey_NoAutocomplete ()
     {
         TextField tf = GetTextFieldsInViewSuggesting ("fish");
-        View.SetClipToScreen ();
+
         // f is typed and suggestion is "fish"
         Application.Driver?.SendKeys ('f', ConsoleKey.F, false, false, false);
+        View.SetClipToScreen ();
         tf.Draw ();
+        View.SetClipToScreen ();
         tf.PositionCursor ();
         DriverAssert.AssertDriverContentsAre ("fish", output);
         Assert.Equal ("f", tf.Text);
@@ -22,8 +24,8 @@ public class AppendAutocompleteTests (ITestOutputHelper output)
         Application.Driver?.SendKeys ('e', ConsoleKey.Escape, false, false, false);
 
         // Suggestion should disappear
-        View.SetClipToScreen ();
         tf.Draw ();
+        View.SetClipToScreen ();
         DriverAssert.AssertDriverContentsAre ("f", output);
         Assert.Equal ("f", tf.Text);
 
@@ -46,6 +48,7 @@ public class AppendAutocompleteTests (ITestOutputHelper output)
         Application.Driver?.SendKeys ('f', ConsoleKey.F, false, false, false);
         View.SetClipToScreen ();
         tf.Draw ();
+        View.SetClipToScreen ();
         tf.PositionCursor ();
         DriverAssert.AssertDriverContentsAre ("fish", output);
         Assert.Equal ("f", tf.Text);
@@ -54,16 +57,16 @@ public class AppendAutocompleteTests (ITestOutputHelper output)
         Application.Driver?.SendKeys ('\0', ConsoleKey.Escape, false, false, false);
 
         // Suggestion should disappear
-        View.SetClipToScreen ();
         tf.Draw ();
         DriverAssert.AssertDriverContentsAre ("f", output);
         Assert.Equal ("f", tf.Text);
 
         // Should reappear when you press next letter
         Application.Driver?.SendKeys ('i', ConsoleKey.I, false, false, false);
-        tf.PositionCursor ();
         View.SetClipToScreen ();
         tf.Draw ();
+        View.SetClipToScreen ();
+        tf.PositionCursor ();
         DriverAssert.AssertDriverContentsAre ("fish", output);
         Assert.Equal ("fi", tf.Text);
         Application.Top.Dispose ();
@@ -81,6 +84,7 @@ public class AppendAutocompleteTests (ITestOutputHelper output)
         Application.Driver?.SendKeys ('f', ConsoleKey.F, false, false, false);
         View.SetClipToScreen ();
         tf.Draw ();
+        View.SetClipToScreen ();
         tf.PositionCursor ();
         DriverAssert.AssertDriverContentsAre ("fish", output);
         Assert.Equal ("f", tf.Text);
@@ -90,6 +94,7 @@ public class AppendAutocompleteTests (ITestOutputHelper output)
 
         View.SetClipToScreen ();
         tf.Draw ();
+        View.SetClipToScreen ();
         tf.PositionCursor ();
         DriverAssert.AssertDriverContentsAre ("friend", output);
         Assert.Equal ("f", tf.Text);
@@ -98,6 +103,7 @@ public class AppendAutocompleteTests (ITestOutputHelper output)
         Application.Driver?.SendKeys (' ', cycleKey, false, false, false);
         View.SetClipToScreen ();
         tf.Draw ();
+        View.SetClipToScreen ();
         tf.PositionCursor ();
         DriverAssert.AssertDriverContentsAre ("fish", output);
         Assert.Equal ("f", tf.Text);
@@ -114,6 +120,7 @@ public class AppendAutocompleteTests (ITestOutputHelper output)
         Application.Driver?.SendKeys ('f', ConsoleKey.F, false, false, false);
         View.SetClipToScreen ();
         tf.Draw ();
+        View.SetClipToScreen ();
         tf.PositionCursor ();
         DriverAssert.AssertDriverContentsAre ("fish", output);
         Assert.Equal ("f", tf.Text);
@@ -139,6 +146,7 @@ public class AppendAutocompleteTests (ITestOutputHelper output)
         Application.Driver?.SendKeys ('f', ConsoleKey.F, false, false, false);
         View.SetClipToScreen ();
         tf.Draw ();
+        View.SetClipToScreen ();
         tf.PositionCursor ();
         DriverAssert.AssertDriverContentsAre ("fish", output);
         Assert.Equal ("f", tf.Text);
@@ -160,10 +168,11 @@ public class AppendAutocompleteTests (ITestOutputHelper output)
 
         tf.Autocomplete = new AppendAutocomplete (tf);
         var generator = (SingleWordSuggestionGenerator)tf.Autocomplete.SuggestionGenerator;
-        generator.AllSuggestions = new List<string> { "FISH" };
+        generator.AllSuggestions = new() { "FISH" };
 
         View.SetClipToScreen ();
         tf.Draw ();
+        View.SetClipToScreen ();
         tf.PositionCursor ();
         DriverAssert.AssertDriverContentsAre ("", output);
         tf.NewKeyDownEvent (Key.M);
@@ -175,6 +184,7 @@ public class AppendAutocompleteTests (ITestOutputHelper output)
         // Even though there is no match on case we should still get the suggestion
         View.SetClipToScreen ();
         tf.Draw ();
+        View.SetClipToScreen ();
         tf.PositionCursor ();
         DriverAssert.AssertDriverContentsAre ("my fISH", output);
         Assert.Equal ("my f", tf.Text);
@@ -196,17 +206,19 @@ public class AppendAutocompleteTests (ITestOutputHelper output)
 
         tf.Autocomplete = new AppendAutocomplete (tf);
         var generator = (SingleWordSuggestionGenerator)tf.Autocomplete.SuggestionGenerator;
-        generator.AllSuggestions = new List<string> { "fish" };
+        generator.AllSuggestions = new() { "fish" };
 
         View.SetClipToScreen ();
         tf.Draw ();
+        View.SetClipToScreen ();
         tf.PositionCursor ();
         DriverAssert.AssertDriverContentsAre ("", output);
 
-        tf.NewKeyDownEvent (new Key ('f'));
+        tf.NewKeyDownEvent (new ('f'));
 
         View.SetClipToScreen ();
         tf.Draw ();
+        View.SetClipToScreen ();
         tf.PositionCursor ();
         DriverAssert.AssertDriverContentsAre ("fish", output);
         Assert.Equal ("f", tf.Text);
@@ -240,6 +252,7 @@ public class AppendAutocompleteTests (ITestOutputHelper output)
         Application.Driver?.SendKeys ('f', ConsoleKey.F, false, false, false);
         View.SetClipToScreen ();
         tf.Draw ();
+        View.SetClipToScreen ();
         tf.PositionCursor ();
         DriverAssert.AssertDriverContentsAre (expectRender, output);
         Assert.Equal ("f", tf.Text);

+ 37 - 2
Tests/UnitTests/Views/TextViewTests.cs

@@ -4905,23 +4905,29 @@ This is the second line.
         Assert.Equal (Point.Empty, tv.CursorPosition);
         Assert.False (tv.ReadOnly);
         Assert.True (tv.CanFocus);
+        Assert.False (tv.IsSelecting);
 
         var g = (SingleWordSuggestionGenerator)tv.Autocomplete.SuggestionGenerator;
 
         tv.CanFocus = false;
         Assert.True (tv.NewKeyDownEvent (Key.CursorLeft));
+        Assert.False (tv.IsSelecting);
         tv.CanFocus = true;
         Assert.False (tv.NewKeyDownEvent (Key.CursorLeft));
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.CursorRight));
         Assert.Equal (new (1, 0), tv.CursorPosition);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.End.WithCtrl));
         Assert.Equal (2, tv.CurrentRow);
         Assert.Equal (23, tv.CurrentColumn);
         Assert.Equal (tv.CurrentColumn, tv.GetCurrentLine ().Count);
         Assert.Equal (new (23, 2), tv.CursorPosition);
+        Assert.False (tv.IsSelecting);
         Assert.False (tv.NewKeyDownEvent (Key.CursorRight));
         Assert.NotNull (tv.Autocomplete);
         Assert.Empty (g.AllSuggestions);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.F.WithShift));
         tv.Draw ();
 
@@ -4931,6 +4937,7 @@ This is the second line.
                      );
         Assert.Equal (new (24, 2), tv.CursorPosition);
         Assert.Empty (tv.Autocomplete.Suggestions);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.Z.WithCtrl));
         tv.Draw ();
 
@@ -4940,6 +4947,7 @@ This is the second line.
                      );
         Assert.Equal (new (23, 2), tv.CursorPosition);
         Assert.Empty (tv.Autocomplete.Suggestions);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.R.WithCtrl));
         tv.Draw ();
 
@@ -4949,6 +4957,7 @@ This is the second line.
                      );
         Assert.Equal (new (24, 2), tv.CursorPosition);
         Assert.Empty (tv.Autocomplete.Suggestions);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.Backspace));
 
         Assert.Equal (
@@ -4969,6 +4978,7 @@ This is the second line.
         Assert.Equal ("line", g.AllSuggestions [4]);
         Assert.Equal ("second", g.AllSuggestions [5]);
         Assert.Equal ("third", g.AllSuggestions [^1]);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.F.WithShift));
         tv.Draw ();
 
@@ -4979,6 +4989,7 @@ This is the second line.
         Assert.Equal (new (24, 2), tv.CursorPosition);
         Assert.Single (tv.Autocomplete.Suggestions);
         Assert.Equal ("first", tv.Autocomplete.Suggestions [0].Replacement);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.Enter));
 
         Assert.Equal (
@@ -4992,68 +5003,85 @@ This is the second line.
         tv.Autocomplete.ClearSuggestions ();
         Assert.Empty (g.AllSuggestions);
         Assert.Empty (tv.Autocomplete.Suggestions);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.PageUp));
         Assert.Equal (24, tv.GetCurrentLine ().Count);
         Assert.Equal (new (24, 1), tv.CursorPosition);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (new (Key.PageUp)));
         Assert.Equal (23, tv.GetCurrentLine ().Count);
         Assert.Equal (new (23, 0), tv.CursorPosition);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.PageDown));
         Assert.Equal (24, tv.GetCurrentLine ().Count);
         Assert.Equal (new (23, 1), tv.CursorPosition); // gets the previous length
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.V.WithCtrl));
         Assert.Equal (28, tv.GetCurrentLine ().Count);
         Assert.Equal (new (23, 2), tv.CursorPosition); // gets the previous length
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal ("", tv.SelectedText);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.PageUp.WithShift));
         Assert.Equal (24, tv.GetCurrentLine ().Count);
         Assert.Equal (new (23, 1), tv.CursorPosition); // gets the previous length
         Assert.Equal (24 + Environment.NewLine.Length, tv.SelectedLength);
         Assert.Equal ($".{Environment.NewLine}This is the third line.", tv.SelectedText);
+        Assert.True (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.PageDown.WithShift));
         Assert.Equal (28, tv.GetCurrentLine ().Count);
         Assert.Equal (new (23, 2), tv.CursorPosition); // gets the previous length
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal ("", tv.SelectedText);
+        Assert.True (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.Home.WithCtrl));
         Assert.Equal (Point.Empty, tv.CursorPosition);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.N.WithCtrl));
         Assert.Equal (new (0, 1), tv.CursorPosition);
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal ("", tv.SelectedText);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.P.WithCtrl));
         Assert.Equal (Point.Empty, tv.CursorPosition);
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal ("", tv.SelectedText);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.CursorDown));
         Assert.Equal (new (0, 1), tv.CursorPosition);
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal ("", tv.SelectedText);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.CursorUp));
         Assert.Equal (Point.Empty, tv.CursorPosition);
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal ("", tv.SelectedText);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.CursorDown.WithShift));
         Assert.Equal (new (0, 1), tv.CursorPosition);
         Assert.Equal (23 + Environment.NewLine.Length, tv.SelectedLength);
         Assert.Equal ($"This is the first line.{Environment.NewLine}", tv.SelectedText);
+        Assert.True (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.CursorUp.WithShift));
         Assert.Equal (Point.Empty, tv.CursorPosition);
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal ("", tv.SelectedText);
+        Assert.True (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.F.WithCtrl));
         Assert.Equal (new (1, 0), tv.CursorPosition);
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal ("", tv.SelectedText);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.B.WithCtrl));
         Assert.Equal (Point.Empty, tv.CursorPosition);
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal ("", tv.SelectedText);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.CursorRight));
         Assert.Equal (new (1, 0), tv.CursorPosition);
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal ("", tv.SelectedText);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.CursorLeft));
         Assert.Equal (Point.Empty, tv.CursorPosition);
         Assert.Equal (0, tv.SelectedLength);
@@ -5103,6 +5131,7 @@ This is the second line.
                       tv.Text
                      );
         Assert.Equal (new (21, 0), tv.CursorPosition);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.Backspace));
 
         Assert.Equal (
@@ -5110,6 +5139,7 @@ This is the second line.
                       tv.Text
                      );
         Assert.Equal (new (20, 0), tv.CursorPosition);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.Backspace));
 
         Assert.Equal (
@@ -5117,6 +5147,7 @@ This is the second line.
                       tv.Text
                      );
         Assert.Equal (new (19, 0), tv.CursorPosition);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.Home));
         Assert.Equal (Point.Empty, tv.CursorPosition);
         Assert.Equal (0, tv.SelectedLength);
@@ -5421,7 +5452,6 @@ This is the second line.
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal ("", tv.SelectedText);
         Assert.False (tv.IsSelecting);
-        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.Backspace.WithCtrl));
         Assert.Equal ($"This is the second line.{Environment.NewLine}This is the third ", tv.Text);
         Assert.Equal (new (18, 1), tv.CursorPosition);
@@ -5493,6 +5523,7 @@ This is the second line.
         Assert.False (tv.Used);
         Assert.True (tv.AllowsTab);
         Assert.Equal (new (18, 2), tv.CursorPosition);
+        Assert.True (tv.IsSelecting);
         tv.AllowsTab = false;
         Assert.False (tv.NewKeyDownEvent (Key.Tab));
 
@@ -5511,6 +5542,7 @@ This is the second line.
                       $"{Environment.NewLine}This is the second line.{Environment.NewLine}This is the third \t",
                       tv.Text
                      );
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.AllowsTab);
         tv.AllowsTab = false;
         Assert.False (tv.NewKeyDownEvent (Key.Tab.WithShift));
@@ -5519,6 +5551,7 @@ This is the second line.
                       $"{Environment.NewLine}This is the second line.{Environment.NewLine}This is the third \t",
                       tv.Text
                      );
+        Assert.False (tv.IsSelecting);
         Assert.False (tv.AllowsTab);
         tv.AllowsTab = true;
         Assert.True (tv.NewKeyDownEvent (Key.Tab.WithShift));
@@ -5527,6 +5560,7 @@ This is the second line.
                       $"{Environment.NewLine}This is the second line.{Environment.NewLine}This is the third ",
                       tv.Text
                      );
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.AllowsTab);
         Assert.False (tv.NewKeyDownEvent (Key.F6));
         Assert.False (tv.NewKeyDownEvent (Application.NextTabGroupKey));
@@ -5535,6 +5569,7 @@ This is the second line.
 
         Assert.True (tv.NewKeyDownEvent (PopoverMenu.DefaultKey));
         Assert.True (tv.ContextMenu != null && tv.ContextMenu.Visible);
+        Assert.False (tv.IsSelecting);
         top.Dispose ();
     }
 
@@ -7105,7 +7140,7 @@ line.
         Assert.True (tv.NewMouseEvent (new () { Position = new (0, 3), Flags = MouseFlags.Button1Pressed }));
         tv.Draw ();
         Assert.Equal (new (0, 3), tv.CursorPosition);
-        Assert.Equal (new (13, 0), cp);
+        Assert.Equal (new (12, 0), cp);
 
         DriverAssert.AssertDriverContentsWithFrameAre (
                                                        @"