2
0
Эх сурвалжийг харах

Merge branch 'v2_develop' of tig:tig/Terminal.Gui into v2_develop

Tig 3 сар өмнө
parent
commit
dea967612b
42 өөрчлөгдсөн 587 нэмэгдсэн , 326 устгасан
  1. 0 1
      Examples/UICatalog/Scenarios/VkeyPacketSimulator.cs
  2. 9 11
      Terminal.Gui/Application/Application.Run.cs
  3. 15 1
      Terminal.Gui/Application/Application.Toplevel.cs
  4. 11 10
      Terminal.Gui/Configuration/ConfigurationManager.cs
  5. 22 11
      Terminal.Gui/Configuration/DeepCloner.cs
  6. 1 1
      Terminal.Gui/Configuration/SourcesManager.cs
  7. 4 0
      Terminal.Gui/Configuration/ThemeManager.cs
  8. 1 1
      Terminal.Gui/ConsoleDrivers/ConsoleKeyMapping.cs
  9. 10 1
      Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
  10. 18 8
      Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs
  11. 0 2
      Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs
  12. 0 1
      Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
  13. 33 8
      Terminal.Gui/ConsoleDrivers/V2/WindowsInputProcessor.cs
  14. 0 1
      Terminal.Gui/ConsoleDrivers/V2/WindowsKeyConverter.cs
  15. 0 1
      Terminal.Gui/ConsoleDrivers/WindowsDriver/WindowsConsole.cs
  16. 32 27
      Terminal.Gui/ConsoleDrivers/WindowsDriver/WindowsDriver.cs
  17. 1 1
      Terminal.Gui/Text/Autocomplete/AutocompleteBase.cs
  18. 4 2
      Terminal.Gui/Text/Autocomplete/PopupAutocomplete.cs
  19. 0 8
      Terminal.Gui/View/View.Layout.cs
  20. 2 1
      Terminal.Gui/Views/TextInput/TextField.cs
  21. 3 1
      Terminal.Gui/Views/TextInput/TextModel.cs
  22. 37 16
      Terminal.Gui/Views/TextInput/TextView.cs
  23. 0 1
      Tests/TerminalGuiFluentTesting/GuiTestContext.cs
  24. 12 12
      Tests/UnitTests/Configuration/ConfigurationMangerTests.cs
  25. 142 12
      Tests/UnitTests/Configuration/SettingsScopeTests.cs
  26. 1 1
      Tests/UnitTests/Configuration/ThemeManagerTests.cs
  27. 11 12
      Tests/UnitTests/ConsoleDrivers/ConsoleKeyMappingTests.cs
  28. 132 38
      Tests/UnitTests/ConsoleDrivers/V2/WindowsInputProcessorTests.cs
  29. 20 7
      Tests/UnitTests/Views/AppendAutocompleteTests.cs
  30. 37 2
      Tests/UnitTests/Views/TextViewTests.cs
  31. 0 1
      Tests/UnitTests/Views/ToplevelTests.cs
  32. 0 119
      Tests/UnitTestsParallelizable/Configuration/DeepClonerTests.cs
  33. 0 1
      Tests/UnitTestsParallelizable/Configuration/SourcesManagerTests.cs
  34. 1 1
      docfx/docs/drawing.md
  35. 1 1
      docfx/docs/events.md
  36. 1 1
      docfx/docs/index.md
  37. 1 1
      docfx/docs/keyboard.md
  38. 1 1
      docfx/docs/mouse.md
  39. 23 0
      docfx/docs/newinv2.md
  40. 1 1
      docfx/docs/toc.yml
  41. BIN
      local_packages/Terminal.Gui.2.0.0.nupkg
  42. BIN
      local_packages/Terminal.Gui.2.0.0.snupkg

+ 0 - 1
Examples/UICatalog/Scenarios/VkeyPacketSimulator.cs

@@ -3,7 +3,6 @@ using System.Collections.Generic;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Terminal.Gui;
 using Terminal.Gui;
-using Terminal.Gui.ConsoleDrivers;
 
 
 namespace UICatalog.Scenarios;
 namespace UICatalog.Scenarios;
 
 

+ 9 - 11
Terminal.Gui/Application/Application.Run.cs

@@ -150,10 +150,10 @@ public static partial class Application // Run (Begin, Run, End, Stop)
                 }
                 }
             }
             }
 
 
-            if (TopLevels.FindDuplicates (new ToplevelEqualityComparer ()).Count > 0)
-            {
-                throw new ArgumentException ("There are duplicates Toplevel IDs");
-            }
+            //if (TopLevels.FindDuplicates (new ToplevelEqualityComparer ()).Count > 0)
+            //{
+            //    throw new ArgumentException ("There are duplicates Toplevel IDs");
+            //}
         }
         }
 
 
         if (Top is null)
         if (Top is null)
@@ -588,25 +588,23 @@ public static partial class Application // Run (Begin, Run, End, Stop)
 
 
         // End the RunState.Toplevel
         // End the RunState.Toplevel
         // First, take it off the Toplevel Stack
         // First, take it off the Toplevel Stack
-        if (TopLevels.Count > 0)
+        if (TopLevels.TryPop (out Toplevel? topOfStack))
         {
         {
-            if (TopLevels.Peek () != runState.Toplevel)
+            if (topOfStack != runState.Toplevel)
             {
             {
                 // If the top of the stack is not the RunState.Toplevel then
                 // If the top of the stack is not the RunState.Toplevel then
                 // this call to End is not balanced with the call to Begin that started the RunState
                 // this call to End is not balanced with the call to Begin that started the RunState
                 throw new ArgumentException ("End must be balanced with calls to Begin");
                 throw new ArgumentException ("End must be balanced with calls to Begin");
             }
             }
-
-            TopLevels.Pop ();
         }
         }
 
 
         // Notify that it is closing
         // Notify that it is closing
         runState.Toplevel?.OnClosed (runState.Toplevel);
         runState.Toplevel?.OnClosed (runState.Toplevel);
 
 
-        if (TopLevels.Count > 0)
+        if (TopLevels.TryPeek (out Toplevel? newTop))
         {
         {
-            Top = TopLevels.Peek ();
-            Top.SetNeedsDraw ();
+            Top = newTop;
+            Top?.SetNeedsDraw ();
         }
         }
 
 
         if (runState.Toplevel is { HasFocus: true })
         if (runState.Toplevel is { HasFocus: true })

+ 15 - 1
Terminal.Gui/Application/Application.Toplevel.cs

@@ -1,12 +1,26 @@
 #nullable enable
 #nullable enable
+using System.Collections.Concurrent;
+
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
 public static partial class Application // Toplevel handling
 public static partial class Application // Toplevel handling
 {
 {
     // BUGBUG: Technically, this is not the full lst of TopLevels. There be dragons here, e.g. see how Toplevel.Id is used. What
     // BUGBUG: Technically, this is not the full lst of TopLevels. There be dragons here, e.g. see how Toplevel.Id is used. What
 
 
+    private static readonly ConcurrentStack<Toplevel> _topLevels = new ();
+    private static readonly object _topLevelsLock = new ();
+
     /// <summary>Holds the stack of TopLevel views.</summary>
     /// <summary>Holds the stack of TopLevel views.</summary>
-    internal static Stack<Toplevel> TopLevels { get; } = new ();
+    internal static ConcurrentStack<Toplevel> TopLevels
+    {
+        get
+        {
+            lock (_topLevelsLock)
+            {
+                return _topLevels;
+            }
+        }
+    }
 
 
     /// <summary>The <see cref="Toplevel"/> that is currently active.</summary>
     /// <summary>The <see cref="Toplevel"/> that is currently active.</summary>
     /// <value>The top.</value>
     /// <value>The top.</value>

+ 11 - 10
Terminal.Gui/Configuration/ConfigurationManager.cs

@@ -8,7 +8,6 @@ using System.Reflection;
 using System.Text.Encodings.Web;
 using System.Text.Encodings.Web;
 using System.Text.Json;
 using System.Text.Json;
 using System.Text.Json.Serialization;
 using System.Text.Json.Serialization;
-using Terminal.Gui.Configuration;
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
@@ -150,7 +149,7 @@ public static class ConfigurationManager
     private static ImmutableSortedDictionary<string, ConfigProperty>? _uninitializedConfigPropertiesCache;
     private static ImmutableSortedDictionary<string, ConfigProperty>? _uninitializedConfigPropertiesCache;
 
 
 #pragma warning disable IDE1006 // Naming Styles
 #pragma warning disable IDE1006 // Naming Styles
-    private static readonly object __uninitializedConfigPropertiesCacheCacheLock = new ();
+    private static readonly object _uninitializedConfigPropertiesCacheCacheLock = new ();
 #pragma warning restore IDE1006 // Naming Styles
 #pragma warning restore IDE1006 // Naming Styles
 
 
     /// <summary>
     /// <summary>
@@ -181,7 +180,7 @@ public static class ConfigurationManager
         ConfigProperty.Initialize ();
         ConfigProperty.Initialize ();
 
 
         // Cache all configuration properties
         // Cache all configuration properties
-        lock (__uninitializedConfigPropertiesCacheCacheLock)
+        lock (_uninitializedConfigPropertiesCacheCacheLock)
         {
         {
             // _allConfigProperties: for ordered, iterable access (LINQ-friendly)
             // _allConfigProperties: for ordered, iterable access (LINQ-friendly)
             // _frozenConfigPropertyCache: for high-speed key lookup (frozen)
             // _frozenConfigPropertyCache: for high-speed key lookup (frozen)
@@ -777,15 +776,17 @@ public static class ConfigurationManager
             return _uninitializedConfigPropertiesCache;
             return _uninitializedConfigPropertiesCache;
         }
         }
 
 
-        // Filter properties by scope using the cached ScopeType property instead of reflection
-        IEnumerable<KeyValuePair<string, ConfigProperty>>? filtered = _uninitializedConfigPropertiesCache?.Where (cp => cp.Value.ScopeType == scopeType);
-
-        Debug.Assert (filtered is { });
+        lock (_uninitializedConfigPropertiesCacheCacheLock)
+        {
+            // Filter properties by scope using the cached ScopeType property instead of reflection
+            IEnumerable<KeyValuePair<string, ConfigProperty>>? filtered = _uninitializedConfigPropertiesCache?.Where (cp => cp.Value.ScopeType == scopeType);
 
 
-        IEnumerable<KeyValuePair<string, ConfigProperty>> configPropertiesByScope = filtered as KeyValuePair<string, ConfigProperty> [] ?? filtered.ToArray ();
-        Debug.Assert (configPropertiesByScope.All (v => !v.Value.HasValue));
+            Debug.Assert (filtered is { });
 
 
-        return configPropertiesByScope;
+            IEnumerable<KeyValuePair<string, ConfigProperty>> configPropertiesByScope = filtered as KeyValuePair<string, ConfigProperty> [] ?? filtered.ToArray ();
+            Debug.Assert (configPropertiesByScope.All (v => !v.Value.HasValue));
+            return configPropertiesByScope;
+        }
     }
     }
 
 
     /// <summary>
     /// <summary>

+ 22 - 11
Terminal.Gui/Configuration/DeepCloner.cs

@@ -285,21 +285,32 @@ public static class DeepCloner
         IDictionary tempDict = CreateDictionaryInstance (dictType, comparer);
         IDictionary tempDict = CreateDictionaryInstance (dictType, comparer);
         visited.TryAdd (source, tempDict);
         visited.TryAdd (source, tempDict);
 
 
-        // Clone all key-value pairs
-        foreach (object? key in sourceDict.Keys)
-        {
-            object? clonedKey = DeepCloneInternal (key, visited);
-            object? clonedValue = DeepCloneInternal (sourceDict [key], visited);
 
 
-            if (tempDict.Contains (clonedKey!))
-            {
-                tempDict [clonedKey!] = clonedValue;
-            }
-            else
+        object? lastKey = null;
+        try
+        {
+            // Clone all key-value pairs
+            foreach (object? key in sourceDict.Keys)
             {
             {
-                tempDict.Add (clonedKey!, clonedValue);
+                lastKey = key;
+                object? clonedKey = DeepCloneInternal (key, visited);
+                object? clonedValue = DeepCloneInternal (sourceDict [key], visited);
+
+                if (tempDict.Contains (clonedKey!))
+                {
+                    tempDict [clonedKey!] = clonedValue;
+                }
+                else
+                {
+                    tempDict.Add (clonedKey!, clonedValue);
+                }
             }
             }
         }
         }
+        catch (InvalidOperationException ex)
+        {
+            // Handle cases where the dictionary is modified during enumeration
+            throw new InvalidOperationException ($"Error cloning dictionary ({source}) (last key was \"{lastKey}\"). Ensure the source dictionary is not modified during cloning.", ex);
+        }
 
 
         // If the original dictionary type has a parameterless constructor, create a new instance
         // If the original dictionary type has a parameterless constructor, create a new instance
         if (type.GetConstructor (Type.EmptyTypes) != null)
         if (type.GetConstructor (Type.EmptyTypes) != null)

+ 1 - 1
Terminal.Gui/Configuration/SourcesManager.cs

@@ -4,7 +4,7 @@ using System.Diagnostics.CodeAnalysis;
 using System.Reflection;
 using System.Reflection;
 using System.Text.Json;
 using System.Text.Json;
 
 
-namespace Terminal.Gui.Configuration;
+namespace Terminal.Gui;
 
 
 /// <summary>
 /// <summary>
 ///    Manages the <see cref="ConfigurationManager"/> Sources and provides the API for loading them. Source is a location where a configuration can be stored. Sources are defined in <see cref="ConfigLocations"/>.
 ///    Manages the <see cref="ConfigurationManager"/> Sources and provides the API for loading them. Source is a location where a configuration can be stored. Sources are defined in <see cref="ConfigLocations"/>.

+ 4 - 0
Terminal.Gui/Configuration/ThemeManager.cs

@@ -101,6 +101,10 @@ public static class ThemeManager
     /// <returns></returns>
     /// <returns></returns>
     public static string GetCurrentThemeName () { return Theme!; }
     public static string GetCurrentThemeName () { return Theme!; }
 
 
+    // TODO: Add a lock around Theme and Themes
+    // TODO: For now, this test can't run in parallel with other tests that access Theme or Themes.
+    // TODO: ThemeScopeList_WithThemes_ClonesSuccessfully
+
     /// <summary>
     /// <summary>
     ///     Gets the Themes dictionary. <see cref="GetThemes"/> is preferred.
     ///     Gets the Themes dictionary. <see cref="GetThemes"/> is preferred.
     ///     The backing store is <c><see cref="ConfigurationManager.Settings"/> ["Themes"]</c>.
     ///     The backing store is <c><see cref="ConfigurationManager.Settings"/> ["Themes"]</c>.

+ 1 - 1
Terminal.Gui/ConsoleDrivers/ConsoleKeyMapping.cs

@@ -1,7 +1,7 @@
 using System.Globalization;
 using System.Globalization;
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
 
 
-namespace Terminal.Gui.ConsoleDrivers;
+namespace Terminal.Gui;
 
 
 // QUESTION: This class combines Windows specific code with cross-platform code. Should this be split into two classes?
 // QUESTION: This class combines Windows specific code with cross-platform code. Should this be split into two classes?
 /// <summary>Helper class to handle the scan code and virtual key from a <see cref="ConsoleKey"/>.</summary>
 /// <summary>Helper class to handle the scan code and virtual key from a <see cref="ConsoleKey"/>.</summary>

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

@@ -4,7 +4,6 @@
 //
 //
 
 
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
-using Terminal.Gui.ConsoleDrivers;
 using Unix.Terminal;
 using Unix.Terminal;
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
@@ -777,6 +776,10 @@ internal class CursesDriver : ConsoleDriver
                 wch -= 60;
                 wch -= 60;
                 k = KeyCode.ShiftMask | KeyCode.AltMask | MapCursesKey (wch);
                 k = KeyCode.ShiftMask | KeyCode.AltMask | MapCursesKey (wch);
             }
             }
+            else if (wch == 520) // Ctrl+Delete
+            {
+                k = KeyCode.CtrlMask | KeyCode.Delete;
+            }
 
 
             OnKeyDown (new Key (k));
             OnKeyDown (new Key (k));
             OnKeyUp (new Key (k));
             OnKeyUp (new Key (k));
@@ -878,6 +881,12 @@ internal class CursesDriver : ConsoleDriver
             OnKeyDown (key);
             OnKeyDown (key);
             OnKeyUp (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)
         else if (wch == Curses.KeyTab)
         {
         {
             k = MapCursesKey (wch);
             k = MapCursesKey (wch);

+ 18 - 8
Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs

@@ -1,6 +1,5 @@
 #nullable enable
 #nullable enable
 using System.Globalization;
 using System.Globalization;
-using static Terminal.Gui.ConsoleDrivers.ConsoleKeyMapping;
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
@@ -1123,6 +1122,17 @@ public static class EscSeqUtils
                                              (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0,
                                              (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0,
                                              true);
                                              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)
                 else if (consoleKeyInfo.Key == 0)
                 {
                 {
                     key = (ConsoleKey)(char)(consoleKeyInfo.KeyChar + (uint)ConsoleKey.A - 1);
                     key = (ConsoleKey)(char)(consoleKeyInfo.KeyChar + (uint)ConsoleKey.A - 1);
@@ -1148,7 +1158,7 @@ public static class EscSeqUtils
 
 
                 break;
                 break;
             default:
             default:
-                uint ck = MapKeyCodeToConsoleKey ((KeyCode)consoleKeyInfo.KeyChar, out bool isConsoleKey);
+                uint ck = ConsoleKeyMapping.MapKeyCodeToConsoleKey ((KeyCode)consoleKeyInfo.KeyChar, out bool isConsoleKey);
 
 
                 if (isConsoleKey)
                 if (isConsoleKey)
                 {
                 {
@@ -1404,12 +1414,12 @@ public static class EscSeqUtils
                 if (keyInfo.Modifiers != ConsoleModifiers.Shift)
                 if (keyInfo.Modifiers != ConsoleModifiers.Shift)
                 {
                 {
                     // If Shift wasn't down we don't need to do anything but return the keyInfo.KeyChar
                     // If Shift wasn't down we don't need to do anything but return the keyInfo.KeyChar
-                    return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)keyInfo.KeyChar);
+                    return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)keyInfo.KeyChar);
                 }
                 }
 
 
                 // Strip off Shift - We got here because they KeyChar from Windows is the shifted char (e.g. "Ç")
                 // Strip off Shift - We got here because they KeyChar from Windows is the shifted char (e.g. "Ç")
                 // and passing on Shift would be redundant.
                 // and passing on Shift would be redundant.
-                return MapToKeyCodeModifiers (keyInfo.Modifiers & ~ConsoleModifiers.Shift, (KeyCode)keyInfo.KeyChar);
+                return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers & ~ConsoleModifiers.Shift, (KeyCode)keyInfo.KeyChar);
         }
         }
 
 
         // Handle control keys whose VK codes match the related ASCII value (those below ASCII 33) like ESC
         // Handle control keys whose VK codes match the related ASCII value (those below ASCII 33) like ESC
@@ -1420,14 +1430,14 @@ public static class EscSeqUtils
                 return KeyCode.Tab;
                 return KeyCode.Tab;
             }
             }
 
 
-            return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)(uint)keyInfo.Key);
+            return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)(uint)keyInfo.Key);
         }
         }
 
 
         // Handle control keys (e.g. CursorUp)
         // Handle control keys (e.g. CursorUp)
         if (keyInfo.Key != ConsoleKey.None
         if (keyInfo.Key != ConsoleKey.None
             && Enum.IsDefined (typeof (KeyCode), (uint)keyInfo.Key + (uint)KeyCode.MaxCodePoint))
             && Enum.IsDefined (typeof (KeyCode), (uint)keyInfo.Key + (uint)KeyCode.MaxCodePoint))
         {
         {
-            return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)((uint)keyInfo.Key + (uint)KeyCode.MaxCodePoint));
+            return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)((uint)keyInfo.Key + (uint)KeyCode.MaxCodePoint));
         }
         }
 
 
         if ((ConsoleKey)keyInfo.KeyChar is >= ConsoleKey.A and <= ConsoleKey.Z)
         if ((ConsoleKey)keyInfo.KeyChar is >= ConsoleKey.A and <= ConsoleKey.Z)
@@ -1458,7 +1468,7 @@ public static class EscSeqUtils
                 || keyInfo.Modifiers.HasFlag (ConsoleModifiers.Control))
                 || keyInfo.Modifiers.HasFlag (ConsoleModifiers.Control))
             {
             {
                 // NetDriver doesn't support Shift-Ctrl/Shift-Alt combos
                 // NetDriver doesn't support Shift-Ctrl/Shift-Alt combos
-                return MapToKeyCodeModifiers (keyInfo.Modifiers & ~ConsoleModifiers.Shift, (KeyCode)keyInfo.Key);
+                return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers & ~ConsoleModifiers.Shift, (KeyCode)keyInfo.Key);
             }
             }
 
 
             if (keyInfo.Modifiers == ConsoleModifiers.Shift)
             if (keyInfo.Modifiers == ConsoleModifiers.Shift)
@@ -1473,7 +1483,7 @@ public static class EscSeqUtils
             return (KeyCode)keyInfo.Key;
             return (KeyCode)keyInfo.Key;
         }
         }
 
 
-        return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)keyInfo.KeyChar);
+        return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)keyInfo.KeyChar);
     }
     }
 
 
     private static async Task ProcessButtonClickedAsync ()
     private static async Task ProcessButtonClickedAsync ()

+ 0 - 2
Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs

@@ -2,8 +2,6 @@
 // FakeConsole.cs: A fake .NET Windows Console API implementation for unit tests.
 // FakeConsole.cs: A fake .NET Windows Console API implementation for unit tests.
 //
 //
 
 
-using Terminal.Gui.ConsoleDrivers;
-
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
 #pragma warning disable RCS1138 // Add summary to documentation comment.
 #pragma warning disable RCS1138 // Add summary to documentation comment.

+ 0 - 1
Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs

@@ -4,7 +4,6 @@
 
 
 using System.Diagnostics;
 using System.Diagnostics;
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
-using Terminal.Gui.ConsoleDrivers;
 
 
 // Alias Console to MockConsole so we don't accidentally use Console
 // Alias Console to MockConsole so we don't accidentally use Console
 
 

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

@@ -81,7 +81,7 @@ internal class WindowsInputProcessor : InputProcessor<InputRecord>
 
 
     public MouseEventArgs ToDriverMouse (MouseEventRecord e)
     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.Button1Pressed, MouseFlags.Button1Pressed, MouseFlags.Button1Released, 0);
         mouseFlags = UpdateMouseFlags (mouseFlags, e.ButtonState, ButtonState.Button2Pressed, MouseFlags.Button2Pressed, MouseFlags.Button2Released, 1);
         mouseFlags = UpdateMouseFlags (mouseFlags, e.ButtonState, ButtonState.Button2Pressed, MouseFlags.Button2Pressed, MouseFlags.Button2Released, 1);
@@ -100,9 +100,6 @@ internal class WindowsInputProcessor : InputProcessor<InputRecord>
             {
             {
                 mouseFlags |= MouseFlags.Button3Released;
                 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;
                 _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
         var result = new MouseEventArgs
         {
         {
             Position = new (e.MousePosition.X, e.MousePosition.Y),
             Position = new (e.MousePosition.X, e.MousePosition.Y),
             Flags = mouseFlags
             Flags = mouseFlags
         };
         };
 
 
-        // TODO: Return keys too
-
         return result;
         return result;
     }
     }
 
 
@@ -154,8 +181,6 @@ internal class WindowsInputProcessor : InputProcessor<InputRecord>
             {
             {
                 current |= releasedFlag;
                 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;
                 _lastWasPressed [buttonIndex] = false;
             }
             }
         }
         }

+ 0 - 1
Terminal.Gui/ConsoleDrivers/V2/WindowsKeyConverter.cs

@@ -1,5 +1,4 @@
 #nullable enable
 #nullable enable
-using Terminal.Gui.ConsoleDrivers;
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 

+ 0 - 1
Terminal.Gui/ConsoleDrivers/WindowsDriver/WindowsConsole.cs

@@ -2,7 +2,6 @@
 using System.Collections.Concurrent;
 using System.Collections.Concurrent;
 using System.ComponentModel;
 using System.ComponentModel;
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
-using Terminal.Gui.ConsoleDrivers;
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 

+ 32 - 27
Terminal.Gui/ConsoleDrivers/WindowsDriver/WindowsDriver.cs

@@ -19,8 +19,6 @@
 using System.ComponentModel;
 using System.ComponentModel;
 using System.Diagnostics;
 using System.Diagnostics;
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
-using static Terminal.Gui.ConsoleDrivers.ConsoleKeyMapping;
-using static Terminal.Gui.SpinnerStyle;
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
@@ -76,7 +74,7 @@ internal class WindowsDriver : ConsoleDriver
 
 
     public static WindowsConsole.KeyEventRecord FromVKPacketToKeyEventRecord (WindowsConsole.KeyEventRecord keyEvent)
     public static WindowsConsole.KeyEventRecord FromVKPacketToKeyEventRecord (WindowsConsole.KeyEventRecord keyEvent)
     {
     {
-        if (keyEvent.wVirtualKeyCode != (VK)ConsoleKey.Packet)
+        if (keyEvent.wVirtualKeyCode != (ConsoleKeyMapping.VK)ConsoleKey.Packet)
         {
         {
             return keyEvent;
             return keyEvent;
         }
         }
@@ -106,8 +104,8 @@ internal class WindowsDriver : ConsoleDriver
                                            mod.HasFlag (ConsoleModifiers.Shift),
                                            mod.HasFlag (ConsoleModifiers.Shift),
                                            mod.HasFlag (ConsoleModifiers.Alt),
                                            mod.HasFlag (ConsoleModifiers.Alt),
                                            mod.HasFlag (ConsoleModifiers.Control));
                                            mod.HasFlag (ConsoleModifiers.Control));
-        cKeyInfo = DecodeVKPacketToKConsoleKeyInfo (cKeyInfo);
-        uint scanCode = GetScanCodeFromConsoleKeyInfo (cKeyInfo);
+        cKeyInfo = ConsoleKeyMapping.DecodeVKPacketToKConsoleKeyInfo (cKeyInfo);
+        uint scanCode = ConsoleKeyMapping.GetScanCodeFromConsoleKeyInfo (cKeyInfo);
 
 
         return new WindowsConsole.KeyEventRecord
         return new WindowsConsole.KeyEventRecord
         {
         {
@@ -115,7 +113,7 @@ internal class WindowsDriver : ConsoleDriver
             bKeyDown = keyEvent.bKeyDown,
             bKeyDown = keyEvent.bKeyDown,
             dwControlKeyState = keyEvent.dwControlKeyState,
             dwControlKeyState = keyEvent.dwControlKeyState,
             wRepeatCount = keyEvent.wRepeatCount,
             wRepeatCount = keyEvent.wRepeatCount,
-            wVirtualKeyCode = (VK)cKeyInfo.Key,
+            wVirtualKeyCode = (ConsoleKeyMapping.VK)cKeyInfo.Key,
             wVirtualScanCode = (ushort)scanCode
             wVirtualScanCode = (ushort)scanCode
         };
         };
     }
     }
@@ -139,7 +137,7 @@ internal class WindowsDriver : ConsoleDriver
         {
         {
             controlKey |= WindowsConsole.ControlKeyState.ShiftPressed;
             controlKey |= WindowsConsole.ControlKeyState.ShiftPressed;
             keyEvent.UnicodeChar = '\0';
             keyEvent.UnicodeChar = '\0';
-            keyEvent.wVirtualKeyCode = VK.SHIFT;
+            keyEvent.wVirtualKeyCode = ConsoleKeyMapping.VK.SHIFT;
         }
         }
 
 
         if (alt)
         if (alt)
@@ -147,7 +145,7 @@ internal class WindowsDriver : ConsoleDriver
             controlKey |= WindowsConsole.ControlKeyState.LeftAltPressed;
             controlKey |= WindowsConsole.ControlKeyState.LeftAltPressed;
             controlKey |= WindowsConsole.ControlKeyState.RightAltPressed;
             controlKey |= WindowsConsole.ControlKeyState.RightAltPressed;
             keyEvent.UnicodeChar = '\0';
             keyEvent.UnicodeChar = '\0';
-            keyEvent.wVirtualKeyCode = VK.MENU;
+            keyEvent.wVirtualKeyCode = ConsoleKeyMapping.VK.MENU;
         }
         }
 
 
         if (control)
         if (control)
@@ -155,7 +153,7 @@ internal class WindowsDriver : ConsoleDriver
             controlKey |= WindowsConsole.ControlKeyState.LeftControlPressed;
             controlKey |= WindowsConsole.ControlKeyState.LeftControlPressed;
             controlKey |= WindowsConsole.ControlKeyState.RightControlPressed;
             controlKey |= WindowsConsole.ControlKeyState.RightControlPressed;
             keyEvent.UnicodeChar = '\0';
             keyEvent.UnicodeChar = '\0';
-            keyEvent.wVirtualKeyCode = VK.CONTROL;
+            keyEvent.wVirtualKeyCode = ConsoleKeyMapping.VK.CONTROL;
         }
         }
 
 
         keyEvent.dwControlKeyState = controlKey;
         keyEvent.dwControlKeyState = controlKey;
@@ -174,7 +172,7 @@ internal class WindowsDriver : ConsoleDriver
         //} else {
         //} else {
         //	keyEvent.wVirtualKeyCode = '\0';
         //	keyEvent.wVirtualKeyCode = '\0';
         //}
         //}
-        keyEvent.wVirtualKeyCode = (VK)key;
+        keyEvent.wVirtualKeyCode = (ConsoleKeyMapping.VK)key;
 
 
         input.KeyEvent = keyEvent;
         input.KeyEvent = keyEvent;
 
 
@@ -492,7 +490,7 @@ internal class WindowsDriver : ConsoleDriver
         switch (inputEvent.EventType)
         switch (inputEvent.EventType)
         {
         {
             case WindowsConsole.EventType.Key:
             case WindowsConsole.EventType.Key:
-                if (inputEvent.KeyEvent.wVirtualKeyCode == (VK)ConsoleKey.Packet)
+                if (inputEvent.KeyEvent.wVirtualKeyCode == (ConsoleKeyMapping.VK)ConsoleKey.Packet)
                 {
                 {
                     // Used to pass Unicode characters as if they were keystrokes.
                     // Used to pass Unicode characters as if they were keystrokes.
                     // The VK_PACKET key is the low word of a 32-bit
                     // The VK_PACKET key is the low word of a 32-bit
@@ -681,7 +679,7 @@ internal class WindowsDriver : ConsoleDriver
             case ConsoleKey.OemMinus:
             case ConsoleKey.OemMinus:
                 // These virtual key codes are mapped differently depending on the keyboard layout in use.
                 // These virtual key codes are mapped differently depending on the keyboard layout in use.
                 // We use the Win32 API to map them to the correct character.
                 // We use the Win32 API to map them to the correct character.
-                uint mapResult = MapVKtoChar ((VK)keyInfo.Key);
+                uint mapResult = ConsoleKeyMapping.MapVKtoChar ((ConsoleKeyMapping.VK)keyInfo.Key);
 
 
                 if (mapResult == 0)
                 if (mapResult == 0)
                 {
                 {
@@ -738,7 +736,7 @@ internal class WindowsDriver : ConsoleDriver
 
 
                     // Return the mappedChar with modifiers. Because mappedChar is un-shifted, if Shift was down
                     // Return the mappedChar with modifiers. Because mappedChar is un-shifted, if Shift was down
                     // we should keep it
                     // we should keep it
-                    return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)mappedChar);
+                    return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)mappedChar);
                 }
                 }
 
 
                 // KeyChar is printable
                 // KeyChar is printable
@@ -751,12 +749,12 @@ internal class WindowsDriver : ConsoleDriver
                 if (keyInfo.Modifiers != ConsoleModifiers.Shift)
                 if (keyInfo.Modifiers != ConsoleModifiers.Shift)
                 {
                 {
                     // If Shift wasn't down we don't need to do anything but return the mappedChar
                     // If Shift wasn't down we don't need to do anything but return the mappedChar
-                    return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)mappedChar);
+                    return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)mappedChar);
                 }
                 }
 
 
                 // Strip off Shift - We got here because they KeyChar from Windows is the shifted char (e.g. "�")
                 // Strip off Shift - We got here because they KeyChar from Windows is the shifted char (e.g. "�")
                 // and passing on Shift would be redundant.
                 // and passing on Shift would be redundant.
-                return MapToKeyCodeModifiers (keyInfo.Modifiers & ~ConsoleModifiers.Shift, (KeyCode)keyInfo.KeyChar);
+                return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers & ~ConsoleModifiers.Shift, (KeyCode)keyInfo.KeyChar);
         }
         }
 
 
         // A..Z are special cased:
         // A..Z are special cased:
@@ -772,13 +770,13 @@ internal class WindowsDriver : ConsoleDriver
                 // AltGr support - AltGr is equivalent to Ctrl+Alt
                 // AltGr support - AltGr is equivalent to Ctrl+Alt
                 if (keyInfo.Modifiers.HasFlag (ConsoleModifiers.Alt) && keyInfo.Modifiers.HasFlag (ConsoleModifiers.Control))
                 if (keyInfo.Modifiers.HasFlag (ConsoleModifiers.Alt) && keyInfo.Modifiers.HasFlag (ConsoleModifiers.Control))
                 {
                 {
-                    return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)(uint)keyInfo.Key);
+                    return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)(uint)keyInfo.Key);
                 }
                 }
             }
             }
 
 
             if (keyInfo.Modifiers.HasFlag (ConsoleModifiers.Alt) || keyInfo.Modifiers.HasFlag (ConsoleModifiers.Control))
             if (keyInfo.Modifiers.HasFlag (ConsoleModifiers.Alt) || keyInfo.Modifiers.HasFlag (ConsoleModifiers.Control))
             {
             {
-                return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)(uint)keyInfo.Key);
+                return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)(uint)keyInfo.Key);
             }
             }
 
 
             if ((keyInfo.Modifiers == ConsoleModifiers.Shift) ^ keyInfoEx.CapsLock)
             if ((keyInfo.Modifiers == ConsoleModifiers.Shift) ^ keyInfoEx.CapsLock)
@@ -789,11 +787,11 @@ internal class WindowsDriver : ConsoleDriver
                     if (keyInfo.KeyChar <= 'Z')
                     if (keyInfo.KeyChar <= 'Z')
                     {
                     {
                         return (KeyCode)keyInfo.Key | KeyCode.ShiftMask;
                         return (KeyCode)keyInfo.Key | KeyCode.ShiftMask;
-                }
+                    }
 
 
                     // Always return the KeyChar because it may be an Á, À with Oem1, etc
                     // Always return the KeyChar because it may be an Á, À with Oem1, etc
                     return (KeyCode)keyInfo.KeyChar;
                     return (KeyCode)keyInfo.KeyChar;
-            }
+                }
             }
             }
 
 
             if (keyInfo.KeyChar <= 'z')
             if (keyInfo.KeyChar <= 'z')
@@ -806,44 +804,51 @@ internal class WindowsDriver : ConsoleDriver
         }
         }
 
 
         // Handle control keys whose VK codes match the related ASCII value (those below ASCII 33) like ESC
         // 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 (Enum.IsDefined (typeof (KeyCode), (uint)keyInfo.Key))
         {
         {
             // If the key is JUST a modifier, return it as just that key
             // If the key is JUST a modifier, return it as just that key
-            if (keyInfo.Key == (ConsoleKey)VK.SHIFT)
+            if (keyInfo.Key == (ConsoleKey)ConsoleKeyMapping.VK.SHIFT)
             { // Shift 16
             { // Shift 16
                 return KeyCode.ShiftMask;
                 return KeyCode.ShiftMask;
             }
             }
 
 
-            if (keyInfo.Key == (ConsoleKey)VK.CONTROL)
+            if (keyInfo.Key == (ConsoleKey)ConsoleKeyMapping.VK.CONTROL)
             { // Ctrl 17
             { // Ctrl 17
                 return KeyCode.CtrlMask;
                 return KeyCode.CtrlMask;
             }
             }
 
 
-            if (keyInfo.Key == (ConsoleKey)VK.MENU)
+            if (keyInfo.Key == (ConsoleKey)ConsoleKeyMapping.VK.MENU)
             { // Alt 18
             { // Alt 18
                 return KeyCode.AltMask;
                 return KeyCode.AltMask;
             }
             }
 
 
             if (keyInfo.KeyChar == 0)
             if (keyInfo.KeyChar == 0)
             {
             {
-                return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)keyInfo.KeyChar);
+                return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)keyInfo.KeyChar);
+            }
+
+            // Backspace (ASCII 127)
+            if (keyInfo.KeyChar == '\u007f')
+            {
+                return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)keyInfo.Key);
             }
             }
 
 
             if (keyInfo.Key != ConsoleKey.None)
             if (keyInfo.Key != ConsoleKey.None)
             {
             {
-                return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)keyInfo.KeyChar);
+                return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)keyInfo.KeyChar);
             }
             }
 
 
-            return MapToKeyCodeModifiers (keyInfo.Modifiers & ~ConsoleModifiers.Shift, (KeyCode)keyInfo.KeyChar);
+            return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers & ~ConsoleModifiers.Shift, (KeyCode)keyInfo.KeyChar);
         }
         }
 
 
         // Handle control keys (e.g. CursorUp)
         // Handle control keys (e.g. CursorUp)
         if (Enum.IsDefined (typeof (KeyCode), (uint)keyInfo.Key + (uint)KeyCode.MaxCodePoint))
         if (Enum.IsDefined (typeof (KeyCode), (uint)keyInfo.Key + (uint)KeyCode.MaxCodePoint))
         {
         {
-            return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)((uint)keyInfo.Key + (uint)KeyCode.MaxCodePoint));
+            return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)((uint)keyInfo.Key + (uint)KeyCode.MaxCodePoint));
         }
         }
 
 
-        return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)keyInfo.KeyChar);
+        return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)keyInfo.KeyChar);
     }
     }
 
 
     private MouseFlags ProcessButtonClick (WindowsConsole.MouseEventRecord mouseEvent)
     private MouseFlags ProcessButtonClick (WindowsConsole.MouseEventRecord mouseEvent)

+ 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;
     public virtual Key CloseKey { get; set; } = Key.Esc;
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public virtual Key Reopen { get; set; } = Key.Space.WithCtrl.WithAlt;
+    public virtual Key Reopen { get; set; } = Key.Space.WithShift;
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
     public virtual AutocompleteContext Context { get; set; }
     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)
         if (PopupInsideContainer)
         {
         {
             // don't overspill vertically
             // 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
             // There is no space below, lets see if can popup on top
             if (height < Suggestions.Count && HostControl.Viewport.Height - renderAt.Y >= height)
             if (height < Suggestions.Count && HostControl.Viewport.Height - renderAt.Y >= height)
@@ -419,8 +419,9 @@ public abstract partial class PopupAutocomplete : AutocompleteBase
         ClearSuggestions ();
         ClearSuggestions ();
         Visible = false;
         Visible = false;
         _closed = true;
         _closed = true;
-        HostControl?.SetNeedsDraw ();
         //RemovePopupFromTop ();
         //RemovePopupFromTop ();
+        _popup.Visible = false;
+        HostControl?.SetNeedsDraw ();
     }
     }
 
 
     /// <summary>Deletes the text backwards before insert the selected text in the <see cref="HostControl"/>.</summary>
     /// <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;
             Visible = true;
             _closed = false;
             _closed = false;
+            _popup.Visible = true;
             HostControl?.SetNeedsDraw ();
             HostControl?.SetNeedsDraw ();
 
 
             return true;
             return true;

+ 0 - 8
Terminal.Gui/View/View.Layout.cs

@@ -800,14 +800,6 @@ public partial class View // Layout APIs
             SuperView?.SetNeedsLayout ();
             SuperView?.SetNeedsLayout ();
         }
         }
 
 
-        if (SuperView is null)
-        {
-            foreach (Toplevel tl in Application.TopLevels)
-            {
-                // tl.SetNeedsDraw ();
-            }
-        }
-
         if (this is not Adornment adornment)
         if (this is not Adornment adornment)
         {
         {
             return;
             return;

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

@@ -1015,7 +1015,6 @@ public class TextField : View, IDesignable
 
 
         RenderCaption ();
         RenderCaption ();
 
 
-        DrawAutocomplete ();
         _isDrawing = false;
         _isDrawing = false;
 
 
         return true;
         return true;
@@ -1686,6 +1685,8 @@ public class TextField : View, IDesignable
         }
         }
 
 
         GenerateSuggestions ();
         GenerateSuggestions ();
+
+        DrawAutocomplete ();
     }
     }
 
 
     private void 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)))
                     if (nRow != fromRow && (Rune.IsLetterOrDigit (nRune) || Rune.IsPunctuation (nRune) || Rune.IsSymbol (nRune)))
                     {
                     {
+                        List<Cell> line = GetLine (nRow);
+
                         if (lastValidCol > -1)
                         if (lastValidCol > -1)
                         {
                         {
-                            nCol = lastValidCol;
+                            nCol = lastValidCol + Math.Max (lastValidCol, line.Count);
                         }
                         }
 
 
                         return;
                         return;

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

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

+ 0 - 1
Tests/TerminalGuiFluentTesting/GuiTestContext.cs

@@ -2,7 +2,6 @@
 using System.Text;
 using System.Text;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
 using Terminal.Gui;
 using Terminal.Gui;
-using Terminal.Gui.ConsoleDrivers;
 
 
 namespace TerminalGuiFluentTesting;
 namespace TerminalGuiFluentTesting;
 
 

+ 12 - 12
Tests/UnitTests/Configuration/ConfigurationMangerTests.cs

@@ -414,7 +414,7 @@ public class ConfigurationManagerTests (ITestOutputHelper output)
                                 """;
                                 """;
 
 
             // Update default config first (lower precedence)
             // Update default config first (lower precedence)
-            SourcesManager?.Load (Settings, defaultConfig, "default-test", ConfigLocations.LibraryResources);
+            ConfigurationManager.SourcesManager?.Load (Settings, defaultConfig, "default-test", ConfigLocations.LibraryResources);
 
 
             // Then load runtime config, which should override default
             // Then load runtime config, which should override default
             Load (ConfigLocations.Runtime);
             Load (ConfigLocations.Runtime);
@@ -447,7 +447,7 @@ public class ConfigurationManagerTests (ITestOutputHelper output)
             ResetToHardCodedDefaults ();
             ResetToHardCodedDefaults ();
 
 
             // Serialize to a JSON string
             // Serialize to a JSON string
-            string json = SourcesManager?.ToJson (Settings);
+            string json = ConfigurationManager.SourcesManager?.ToJson (Settings);
 
 
             // Write the JSON string to the file
             // Write the JSON string to the file
             File.WriteAllText ("hard_coded_defaults_config.json", json);
             File.WriteAllText ("hard_coded_defaults_config.json", json);
@@ -476,7 +476,7 @@ public class ConfigurationManagerTests (ITestOutputHelper output)
             Enable (ConfigLocations.LibraryResources);
             Enable (ConfigLocations.LibraryResources);
 
 
             // Serialize to a JSON string
             // Serialize to a JSON string
-            string json = SourcesManager?.ToJson (Settings);
+            string json = ConfigurationManager.SourcesManager?.ToJson (Settings);
 
 
             // Write the JSON string to the file
             // Write the JSON string to the file
             File.WriteAllText ("library_defaults_config.json", json);
             File.WriteAllText ("library_defaults_config.json", json);
@@ -688,7 +688,7 @@ public class ConfigurationManagerTests (ITestOutputHelper output)
 				}
 				}
 			}";
 			}";
 
 
-        SourcesManager?.Load (Settings, json, "test", ConfigLocations.Runtime);
+        ConfigurationManager.SourcesManager?.Load (Settings, json, "test", ConfigLocations.Runtime);
 
 
         // AbNormal is not a Scheme attribute
         // AbNormal is not a Scheme attribute
         json = @"
         json = @"
@@ -711,7 +711,7 @@ public class ConfigurationManagerTests (ITestOutputHelper output)
 				}
 				}
 			}";
 			}";
 
 
-        SourcesManager?.Load (Settings, json, "test", ConfigLocations.Runtime);
+        ConfigurationManager.SourcesManager?.Load (Settings, json, "test", ConfigLocations.Runtime);
 
 
         // Modify hotNormal background only
         // Modify hotNormal background only
         json = @"
         json = @"
@@ -733,9 +733,9 @@ public class ConfigurationManagerTests (ITestOutputHelper output)
 				}
 				}
 			}";
 			}";
 
 
-        SourcesManager?.Load (Settings, json, "test", ConfigLocations.Runtime);
+        ConfigurationManager.SourcesManager?.Load (Settings, json, "test", ConfigLocations.Runtime);
 
 
-        SourcesManager?.Load (Settings, "{}}", "test", ConfigLocations.Runtime);
+        ConfigurationManager.SourcesManager?.Load (Settings, "{}}", "test", ConfigLocations.Runtime);
 
 
         Assert.NotEqual (0, _jsonErrors.Length);
         Assert.NotEqual (0, _jsonErrors.Length);
 
 
@@ -773,7 +773,7 @@ public class ConfigurationManagerTests (ITestOutputHelper output)
 				]
 				]
 			}";
 			}";
 
 
-        var jsonException = Assert.Throws<JsonException> (() => SourcesManager?.Load (Settings, json, "test", ConfigLocations.Runtime));
+        var jsonException = Assert.Throws<JsonException> (() => ConfigurationManager.SourcesManager?.Load (Settings, json, "test", ConfigLocations.Runtime));
         Assert.StartsWith ("foreground: \"\"brownish\"\"", jsonException.Message);
         Assert.StartsWith ("foreground: \"\"brownish\"\"", jsonException.Message);
 
 
         // AbNormal is not a Scheme attribute
         // AbNormal is not a Scheme attribute
@@ -797,7 +797,7 @@ public class ConfigurationManagerTests (ITestOutputHelper output)
 				]
 				]
 			}";
 			}";
 
 
-        jsonException = Assert.Throws<JsonException> (() => SourcesManager?.Load (Settings, json, "test", ConfigLocations.Runtime));
+        jsonException = Assert.Throws<JsonException> (() => ConfigurationManager.SourcesManager?.Load (Settings, json, "test", ConfigLocations.Runtime));
         Assert.StartsWith ("AbNormal:", jsonException.Message);
         Assert.StartsWith ("AbNormal:", jsonException.Message);
 
 
         // Modify hotNormal background only
         // Modify hotNormal background only
@@ -820,7 +820,7 @@ public class ConfigurationManagerTests (ITestOutputHelper output)
 				]
 				]
 			}";
 			}";
 
 
-        jsonException = Assert.Throws<JsonException> (() => SourcesManager?.Load (Settings, json, "test", ConfigLocations.Runtime));
+        jsonException = Assert.Throws<JsonException> (() => ConfigurationManager.SourcesManager?.Load (Settings, json, "test", ConfigLocations.Runtime));
         Assert.StartsWith ("background:", jsonException.Message);
         Assert.StartsWith ("background:", jsonException.Message);
 
 
         // Unknown property
         // Unknown property
@@ -829,7 +829,7 @@ public class ConfigurationManagerTests (ITestOutputHelper output)
 				""Unknown"" : ""Not known""
 				""Unknown"" : ""Not known""
 			}";
 			}";
 
 
-        jsonException = Assert.Throws<JsonException> (() => SourcesManager?.Load (Settings, json, "test", ConfigLocations.Runtime));
+        jsonException = Assert.Throws<JsonException> (() => ConfigurationManager.SourcesManager?.Load (Settings, json, "test", ConfigLocations.Runtime));
         Assert.StartsWith ("Unknown:", jsonException.Message);
         Assert.StartsWith ("Unknown:", jsonException.Message);
 
 
         Assert.Equal (0, _jsonErrors.Length);
         Assert.Equal (0, _jsonErrors.Length);
@@ -989,7 +989,7 @@ public class ConfigurationManagerTests (ITestOutputHelper output)
             ResetToCurrentValues ();
             ResetToCurrentValues ();
             ThrowOnJsonErrors = true;
             ThrowOnJsonErrors = true;
 
 
-            SourcesManager?.Load (Settings, json, "UpdateFromJson", ConfigLocations.Runtime);
+            ConfigurationManager.SourcesManager?.Load (Settings, json, "UpdateFromJson", ConfigLocations.Runtime);
 
 
             Assert.Equal ("Default", ThemeManager.Theme);
             Assert.Equal ("Default", ThemeManager.Theme);
 
 

+ 142 - 12
Tests/UnitTests/Configuration/SettingsScopeTests.cs

@@ -1,5 +1,5 @@
-using System.Collections.Concurrent;
-using UnitTests;
+#nullable enable
+using System.Collections.Concurrent;
 using static Terminal.Gui.ConfigurationManager;
 using static Terminal.Gui.ConfigurationManager;
 
 
 namespace Terminal.Gui.ConfigurationTests;
 namespace Terminal.Gui.ConfigurationTests;
@@ -12,7 +12,7 @@ public class SettingsScopeTests
         // arrange
         // arrange
         Enable (ConfigLocations.HardCoded);
         Enable (ConfigLocations.HardCoded);
 
 
-        Assert.Equal (Key.Esc, (Key)Settings! ["Application.QuitKey"].PropertyValue);
+        Assert.Equal (Key.Esc, (Key)Settings! ["Application.QuitKey"].PropertyValue!);
 
 
         ThrowOnJsonErrors = true;
         ThrowOnJsonErrors = true;
 
 
@@ -27,7 +27,7 @@ public class SettingsScopeTests
         Load (ConfigLocations.Runtime);
         Load (ConfigLocations.Runtime);
 
 
         // assert
         // assert
-        Assert.Equal (Key.Q.WithCtrl, (Key)Settings ["Application.QuitKey"].PropertyValue);
+        Assert.Equal (Key.Q.WithCtrl, (Key)Settings ["Application.QuitKey"].PropertyValue!);
 
 
         // clean up
         // clean up
         Disable (resetToHardCodedDefaults: true);
         Disable (resetToHardCodedDefaults: true);
@@ -43,11 +43,11 @@ public class SettingsScopeTests
         ThrowOnJsonErrors = true;
         ThrowOnJsonErrors = true;
 
 
         ConfigProperty themesConfigProperty = Settings! ["Themes"];
         ConfigProperty themesConfigProperty = Settings! ["Themes"];
-        ConcurrentDictionary<string, ThemeScope> dict = themesConfigProperty.PropertyValue as ConcurrentDictionary<string, ThemeScope>;
+        ConcurrentDictionary<string, ThemeScope> dict = (themesConfigProperty.PropertyValue as ConcurrentDictionary<string, ThemeScope>)!;
 
 
         Assert.NotNull (dict);
         Assert.NotNull (dict);
         Assert.Single (dict);
         Assert.Single (dict);
-        Assert.NotEmpty ((ConcurrentDictionary<string, ThemeScope>)themesConfigProperty.PropertyValue);
+        Assert.NotEmpty (((ConcurrentDictionary<string, ThemeScope>)themesConfigProperty.PropertyValue!)!);
 
 
         ThemeScope scope = dict [ThemeManager.DEFAULT_THEME_NAME];
         ThemeScope scope = dict [ThemeManager.DEFAULT_THEME_NAME];
         Assert.NotNull (scope);
         Assert.NotNull (scope);
@@ -111,16 +111,16 @@ public class SettingsScopeTests
         Load (ConfigLocations.LibraryResources);
         Load (ConfigLocations.LibraryResources);
 
 
         // arrange
         // arrange
-        Assert.Equal (Key.Esc, (Key)Settings! ["Application.QuitKey"].PropertyValue);
+        Assert.Equal (Key.Esc, (Key)Settings!["Application.QuitKey"].PropertyValue!);
 
 
         Assert.Equal (
         Assert.Equal (
                       Key.F6,
                       Key.F6,
-                      (Key)Settings ["Application.NextTabGroupKey"].PropertyValue
+                      (Key)Settings["Application.NextTabGroupKey"].PropertyValue!
                      );
                      );
 
 
         Assert.Equal (
         Assert.Equal (
                       Key.F6.WithShift,
                       Key.F6.WithShift,
-                      (Key)Settings ["Application.PrevTabGroupKey"].PropertyValue
+                      (Key)Settings["Application.PrevTabGroupKey"].PropertyValue!
                      );
                      );
 
 
         // act
         // act
@@ -154,9 +154,9 @@ public class SettingsScopeTests
         updatedSettings ["Application.PrevTabGroupKey"].PropertyValue = Key.B;
         updatedSettings ["Application.PrevTabGroupKey"].PropertyValue = Key.B;
 
 
         Settings.UpdateFrom (updatedSettings);
         Settings.UpdateFrom (updatedSettings);
-        Assert.Equal (KeyCode.End, ((Key)Settings ["Application.QuitKey"].PropertyValue).KeyCode);
-        Assert.Equal (KeyCode.F, ((Key)updatedSettings ["Application.NextTabGroupKey"].PropertyValue).KeyCode);
-        Assert.Equal (KeyCode.B, ((Key)updatedSettings ["Application.PrevTabGroupKey"].PropertyValue).KeyCode);
+        Assert.Equal (KeyCode.End, ((Key)Settings["Application.QuitKey"].PropertyValue!).KeyCode);
+        Assert.Equal (KeyCode.F, ((Key)updatedSettings["Application.NextTabGroupKey"].PropertyValue!).KeyCode);
+        Assert.Equal (KeyCode.B, ((Key)updatedSettings["Application.PrevTabGroupKey"].PropertyValue!).KeyCode);
         Disable (resetToHardCodedDefaults: true);
         Disable (resetToHardCodedDefaults: true);
     }
     }
 
 
@@ -218,4 +218,134 @@ public class SettingsScopeTests
 
 
         Disable (resetToHardCodedDefaults: true);
         Disable (resetToHardCodedDefaults: true);
     }
     }
+    
+    private class ConfigPropertyMock
+    {
+        public object? PropertyValue { get; init; }
+        public bool Immutable { get; init; }
+    }
+
+    private class SettingsScopeMock : Dictionary<string, ConfigPropertyMock>
+    {
+        public string? Theme { get; set; }
+    }
+
+
+    [Fact]
+    public void SettingsScopeMockWithKey_CreatesDeepCopy ()
+    {
+        SettingsScopeMock? source = new ()
+        {
+            Theme = "Dark",
+            ["KeyBinding"] = new () { PropertyValue = new Key (KeyCode.A) { Handled = true } },
+            ["Counts"] = new () { PropertyValue = new Dictionary<string, int> { { "X", 1 } } }
+        };
+        SettingsScopeMock? result = DeepCloner.DeepClone (source);
+
+        Assert.NotNull (result);
+        Assert.NotSame (source, result);
+        Assert.Equal (source.Theme, result!.Theme);
+        Assert.NotSame (source ["KeyBinding"], result ["KeyBinding"]);
+        Assert.NotSame (source ["Counts"], result ["Counts"]);
+
+        ConfigPropertyMock clonedKeyProp = result ["KeyBinding"];
+        var clonedKey = (Key)clonedKeyProp.PropertyValue!;
+        Assert.NotSame (source ["KeyBinding"].PropertyValue, clonedKey);
+        Assert.Equal (((Key)source ["KeyBinding"].PropertyValue!).KeyCode, clonedKey.KeyCode);
+        Assert.Equal (((Key)source ["KeyBinding"].PropertyValue!).Handled, clonedKey.Handled);
+
+        Assert.Equal ((Dictionary<string, int>)source ["Counts"].PropertyValue!, (Dictionary<string, int>)result ["Counts"].PropertyValue!);
+
+        // Modify result, ensure source unchanged
+        result.Theme = "Light";
+        clonedKey.Handled = false;
+        ((Dictionary<string, int>)result ["Counts"].PropertyValue!).Add ("Y", 2);
+        Assert.Equal ("Dark", source.Theme);
+        Assert.True (((Key)source ["KeyBinding"].PropertyValue!).Handled);
+        Assert.Single ((Dictionary<string, int>)source ["Counts"].PropertyValue!);
+        Disable (resetToHardCodedDefaults: true);
+    }
+
+    [Fact /*(Skip = "This test randomly fails due to a concurrent change to something. Needs to be moved to non-parallel tests.")*/]
+    public void ThemeScopeList_WithThemes_ClonesSuccessfully ()
+    {
+        // Arrange: Create a ThemeScope and verify a property exists
+        ThemeScope defaultThemeScope = new ThemeScope ();
+        defaultThemeScope.LoadHardCodedDefaults ();
+        Assert.True (defaultThemeScope.ContainsKey ("Button.DefaultHighlightStyle"));
+
+        ThemeScope darkThemeScope = new ThemeScope ();
+        darkThemeScope.LoadHardCodedDefaults ();
+        Assert.True (darkThemeScope.ContainsKey ("Button.DefaultHighlightStyle"));
+
+        // Create a Themes list with two themes
+        List<Dictionary<string, ThemeScope>> themesList =
+        [
+            new () { { "Default", defaultThemeScope } },
+            new () { { "Dark", darkThemeScope } }
+        ];
+
+        // Create a SettingsScope and set the Themes property
+        SettingsScope settingsScope = new SettingsScope ();
+        settingsScope.LoadHardCodedDefaults ();
+        Assert.True (settingsScope.ContainsKey ("Themes"));
+        settingsScope ["Themes"].PropertyValue = themesList;
+
+        // Act
+        SettingsScope? result = DeepCloner.DeepClone (settingsScope);
+
+        // Assert
+        Assert.NotNull (result);
+        Assert.IsType<SettingsScope> (result);
+        SettingsScope resultScope = (SettingsScope)result;
+        Assert.True (resultScope.ContainsKey ("Themes"));
+
+        Assert.NotNull (resultScope ["Themes"].PropertyValue);
+
+        List<Dictionary<string, ThemeScope>> clonedThemes = (List<Dictionary<string, ThemeScope>>)resultScope ["Themes"].PropertyValue!;
+        Assert.Equal (2, clonedThemes.Count);
+        Disable (resetToHardCodedDefaults: true);
+    }
+
+    [Fact]
+    public void Empty_SettingsScope_ClonesSuccessfully ()
+    {
+        // Arrange: Create a SettingsScope 
+        var settingsScope = new SettingsScope ();
+        Assert.True (settingsScope.ContainsKey ("Themes"));
+
+        // Act
+        SettingsScope? result = DeepCloner.DeepClone (settingsScope);
+
+        // Assert
+        Assert.NotNull (result);
+        Assert.IsType<SettingsScope> (result);
+
+        Assert.True (result.ContainsKey ("Themes"));
+        Disable (resetToHardCodedDefaults: true);
+    }
+
+    [Fact]
+    public void SettingsScope_With_Themes_Set_ClonesSuccessfully ()
+    {
+        // Arrange: Create a SettingsScope 
+        var settingsScope = new SettingsScope ();
+        Assert.True (settingsScope.ContainsKey ("Themes"));
+
+        settingsScope ["Themes"].PropertyValue = new List<Dictionary<string, ThemeScope>>
+        {
+            new() { { "Default", new () } },
+            new() { { "Dark", new () } }
+        };
+
+        // Act
+        SettingsScope? result = DeepCloner.DeepClone (settingsScope);
+
+        // Assert
+        Assert.NotNull (result);
+        Assert.IsType<SettingsScope> (result);
+        Assert.True (result.ContainsKey ("Themes"));
+        Assert.NotNull (result ["Themes"].PropertyValue);
+        Disable (resetToHardCodedDefaults: true);
+    }
 }
 }

+ 1 - 1
Tests/UnitTests/Configuration/ThemeManagerTests.cs

@@ -272,7 +272,7 @@ public class ThemeManagerTests (ITestOutputHelper output)
 
 
         output.WriteLine ($"Total Settings Size: {(MemorySizeEstimator.EstimateSize (Settings!)) / 1024} Kb");
         output.WriteLine ($"Total Settings Size: {(MemorySizeEstimator.EstimateSize (Settings!)) / 1024} Kb");
 
 
-        string json = SourcesManager?.ToJson (Settings)!;
+        string json = ConfigurationManager.SourcesManager?.ToJson (Settings)!;
 
 
         // In memory size should be less than the size of the json
         // In memory size should be less than the size of the json
         output.WriteLine ($"JSON size: {json.Length / 1024} Kb");
         output.WriteLine ($"JSON size: {json.Length / 1024} Kb");

+ 11 - 12
Tests/UnitTests/ConsoleDrivers/ConsoleKeyMappingTests.cs

@@ -1,6 +1,5 @@
-using static Terminal.Gui.ConsoleDrivers.ConsoleKeyMapping;
 
 
-namespace Terminal.Gui.ConsoleDrivers;
+namespace Terminal.Gui.ConsoleDriverTests;
 
 
 public class ConsoleKeyMappingTests
 public class ConsoleKeyMappingTests
 {
 {
@@ -365,7 +364,7 @@ public class ConsoleKeyMappingTests
     )
     )
     {
     {
         var consoleKeyInfo = new ConsoleKeyInfo (keyChar, consoleKey, shift, alt, control);
         var consoleKeyInfo = new ConsoleKeyInfo (keyChar, consoleKey, shift, alt, control);
-        KeyCode keyCode = MapConsoleKeyInfoToKeyCode (consoleKeyInfo);
+        KeyCode keyCode = ConsoleKeyMapping.MapConsoleKeyInfoToKeyCode (consoleKeyInfo);
 
 
         Assert.Equal (keyCode, expectedKeyCode);
         Assert.Equal (keyCode, expectedKeyCode);
     }
     }
@@ -395,9 +394,9 @@ public class ConsoleKeyMappingTests
         KeyCode expectedKeyCode
         KeyCode expectedKeyCode
     )
     )
     {
     {
-        ConsoleModifiers modifiers = GetModifiers (shift, alt, control);
+        ConsoleModifiers modifiers = ConsoleKeyMapping.GetModifiers (shift, alt, control);
         var keyCode = (KeyCode)keyChar;
         var keyCode = (KeyCode)keyChar;
-        keyCode = MapToKeyCodeModifiers (modifiers, keyCode);
+        keyCode = ConsoleKeyMapping.MapToKeyCodeModifiers (modifiers, keyCode);
 
 
         Assert.Equal (keyCode, expectedKeyCode);
         Assert.Equal (keyCode, expectedKeyCode);
     }
     }
@@ -414,7 +413,7 @@ public class ConsoleKeyMappingTests
     )
     )
     {
     {
         var consoleKeyInfo = new ConsoleKeyInfo (keyChar, consoleKey, shift, alt, control);
         var consoleKeyInfo = new ConsoleKeyInfo (keyChar, consoleKey, shift, alt, control);
-        uint scanCode = GetScanCodeFromConsoleKeyInfo (consoleKeyInfo);
+        uint scanCode = ConsoleKeyMapping.GetScanCodeFromConsoleKeyInfo (consoleKeyInfo);
 
 
         Assert.Equal (scanCode, expectedScanCode);
         Assert.Equal (scanCode, expectedScanCode);
     }
     }
@@ -492,12 +491,12 @@ public class ConsoleKeyMappingTests
         KeyCode expectedKeyCode
         KeyCode expectedKeyCode
     )
     )
     {
     {
-        ConsoleModifiers modifiers = GetModifiers (true, false, false);
-        uint keyChar = GetKeyChar (unicodeChar, modifiers);
+        ConsoleModifiers modifiers = ConsoleKeyMapping.GetModifiers (true, false, false);
+        uint keyChar = ConsoleKeyMapping.GetKeyChar (unicodeChar, modifiers);
         Assert.Equal (keyChar, expectedKeyChar);
         Assert.Equal (keyChar, expectedKeyChar);
 
 
         var keyCode = (KeyCode)keyChar;
         var keyCode = (KeyCode)keyChar;
-        keyCode = MapToKeyCodeModifiers (modifiers, keyCode);
+        keyCode = ConsoleKeyMapping.MapToKeyCodeModifiers (modifiers, keyCode);
 
 
         Assert.Equal (keyCode, expectedKeyCode);
         Assert.Equal (keyCode, expectedKeyCode);
     }
     }
@@ -541,12 +540,12 @@ public class ConsoleKeyMappingTests
         KeyCode expectedKeyCode
         KeyCode expectedKeyCode
     )
     )
     {
     {
-        ConsoleModifiers modifiers = GetModifiers (false, false, false);
-        uint keyChar = GetKeyChar (unicodeChar, modifiers);
+        ConsoleModifiers modifiers = ConsoleKeyMapping.GetModifiers (false, false, false);
+        uint keyChar = ConsoleKeyMapping.GetKeyChar (unicodeChar, modifiers);
         Assert.Equal (keyChar, expectedKeyChar);
         Assert.Equal (keyChar, expectedKeyChar);
 
 
         var keyCode = (KeyCode)keyChar;
         var keyCode = (KeyCode)keyChar;
-        keyCode = MapToKeyCodeModifiers (modifiers, keyCode);
+        keyCode = ConsoleKeyMapping.MapToKeyCodeModifiers (modifiers, keyCode);
 
 
         Assert.Equal (keyCode, expectedKeyCode);
         Assert.Equal (keyCode, expectedKeyCode);
     }
     }

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

@@ -1,7 +1,8 @@
 using System.Collections.Concurrent;
 using System.Collections.Concurrent;
-using Terminal.Gui.ConsoleDrivers;
 using InputRecord = Terminal.Gui.WindowsConsole.InputRecord;
 using InputRecord = Terminal.Gui.WindowsConsole.InputRecord;
 using ButtonState = Terminal.Gui.WindowsConsole.ButtonState;
 using ButtonState = Terminal.Gui.WindowsConsole.ButtonState;
+using EventFlags = Terminal.Gui.WindowsConsole.EventFlags;
+using ControlKeyState = Terminal.Gui.WindowsConsole.ControlKeyState;
 using MouseEventRecord = Terminal.Gui.WindowsConsole.MouseEventRecord;
 using MouseEventRecord = Terminal.Gui.WindowsConsole.MouseEventRecord;
 
 
 namespace UnitTests.ConsoleDrivers.V2;
 namespace UnitTests.ConsoleDrivers.V2;
@@ -102,7 +103,7 @@ public class WindowsInputProcessorTests
                                MousePosition = new (32, 31),
                                MousePosition = new (32, 31),
                                ButtonState = ButtonState.NoButtonPressed,
                                ButtonState = ButtonState.NoButtonPressed,
                                ControlKeyState = WindowsConsole.ControlKeyState.NoControlKeyPressed,
                                ControlKeyState = WindowsConsole.ControlKeyState.NoControlKeyPressed,
-                               EventFlags = WindowsConsole.EventFlags.MouseMoved
+                               EventFlags = EventFlags.MouseMoved
                            }
                            }
                        });
                        });
 
 
@@ -139,7 +140,7 @@ public class WindowsInputProcessorTests
                                MousePosition = new (32, 31),
                                MousePosition = new (32, 31),
                                ButtonState = state,
                                ButtonState = state,
                                ControlKeyState = WindowsConsole.ControlKeyState.NoControlKeyPressed,
                                ControlKeyState = WindowsConsole.ControlKeyState.NoControlKeyPressed,
-                               EventFlags = WindowsConsole.EventFlags.MouseMoved
+                               EventFlags = EventFlags.MouseMoved
                            }
                            }
                        });
                        });
 
 
@@ -199,9 +200,9 @@ public class WindowsInputProcessorTests
         {
         {
             new []
             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 +210,9 @@ public class WindowsInputProcessorTests
         {
         {
             new []
             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 +220,9 @@ public class WindowsInputProcessorTests
         {
         {
             new []
             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 +230,9 @@ public class WindowsInputProcessorTests
         {
         {
             new []
             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 +240,9 @@ public class WindowsInputProcessorTests
         {
         {
             new []
             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 +252,11 @@ public class WindowsInputProcessorTests
             new []
             new []
             {
             {
                 Tuple.Create (
                 Tuple.Create (
-                              ButtonState.Button1Pressed | ButtonState.Button2Pressed,
+                              ButtonState.Button1Pressed | ButtonState.Button2Pressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed,
                               MouseFlags.Button1Pressed | MouseFlags.Button2Pressed | MouseFlags.ReportMousePosition),
                               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 +265,11 @@ public class WindowsInputProcessorTests
             new []
             new []
             {
             {
                 Tuple.Create (
                 Tuple.Create (
-                              ButtonState.Button3Pressed | ButtonState.Button4Pressed,
+                              ButtonState.Button3Pressed | ButtonState.Button4Pressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed,
                               MouseFlags.Button3Pressed | MouseFlags.Button4Pressed | MouseFlags.ReportMousePosition),
                               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 +279,10 @@ public class WindowsInputProcessorTests
             new []
             new []
             {
             {
                 Tuple.Create (
                 Tuple.Create (
-                              ButtonState.Button1Pressed | ButtonState.Button2Pressed,
+                              ButtonState.Button1Pressed | ButtonState.Button2Pressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed,
                               MouseFlags.Button1Pressed | MouseFlags.Button2Pressed | MouseFlags.ReportMousePosition),
                               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 +291,124 @@ public class WindowsInputProcessorTests
         {
         {
             new []
             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
                 // 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
                 // 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]
     [Theory]
     [MemberData (nameof (MouseFlagTestData))]
     [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 ());
         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);
             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 ()
     public void TestAutoAppend_AfterCloseKey_NoAutocomplete ()
     {
     {
         TextField tf = GetTextFieldsInViewSuggesting ("fish");
         TextField tf = GetTextFieldsInViewSuggesting ("fish");
-        View.SetClipToScreen ();
+
         // f is typed and suggestion is "fish"
         // f is typed and suggestion is "fish"
         Application.Driver?.SendKeys ('f', ConsoleKey.F, false, false, false);
         Application.Driver?.SendKeys ('f', ConsoleKey.F, false, false, false);
+        View.SetClipToScreen ();
         tf.Draw ();
         tf.Draw ();
+        View.SetClipToScreen ();
         tf.PositionCursor ();
         tf.PositionCursor ();
         DriverAssert.AssertDriverContentsAre ("fish", output);
         DriverAssert.AssertDriverContentsAre ("fish", output);
         Assert.Equal ("f", tf.Text);
         Assert.Equal ("f", tf.Text);
@@ -22,8 +24,8 @@ public class AppendAutocompleteTests (ITestOutputHelper output)
         Application.Driver?.SendKeys ('e', ConsoleKey.Escape, false, false, false);
         Application.Driver?.SendKeys ('e', ConsoleKey.Escape, false, false, false);
 
 
         // Suggestion should disappear
         // Suggestion should disappear
-        View.SetClipToScreen ();
         tf.Draw ();
         tf.Draw ();
+        View.SetClipToScreen ();
         DriverAssert.AssertDriverContentsAre ("f", output);
         DriverAssert.AssertDriverContentsAre ("f", output);
         Assert.Equal ("f", tf.Text);
         Assert.Equal ("f", tf.Text);
 
 
@@ -46,6 +48,7 @@ public class AppendAutocompleteTests (ITestOutputHelper output)
         Application.Driver?.SendKeys ('f', ConsoleKey.F, false, false, false);
         Application.Driver?.SendKeys ('f', ConsoleKey.F, false, false, false);
         View.SetClipToScreen ();
         View.SetClipToScreen ();
         tf.Draw ();
         tf.Draw ();
+        View.SetClipToScreen ();
         tf.PositionCursor ();
         tf.PositionCursor ();
         DriverAssert.AssertDriverContentsAre ("fish", output);
         DriverAssert.AssertDriverContentsAre ("fish", output);
         Assert.Equal ("f", tf.Text);
         Assert.Equal ("f", tf.Text);
@@ -54,16 +57,16 @@ public class AppendAutocompleteTests (ITestOutputHelper output)
         Application.Driver?.SendKeys ('\0', ConsoleKey.Escape, false, false, false);
         Application.Driver?.SendKeys ('\0', ConsoleKey.Escape, false, false, false);
 
 
         // Suggestion should disappear
         // Suggestion should disappear
-        View.SetClipToScreen ();
         tf.Draw ();
         tf.Draw ();
         DriverAssert.AssertDriverContentsAre ("f", output);
         DriverAssert.AssertDriverContentsAre ("f", output);
         Assert.Equal ("f", tf.Text);
         Assert.Equal ("f", tf.Text);
 
 
         // Should reappear when you press next letter
         // Should reappear when you press next letter
         Application.Driver?.SendKeys ('i', ConsoleKey.I, false, false, false);
         Application.Driver?.SendKeys ('i', ConsoleKey.I, false, false, false);
-        tf.PositionCursor ();
         View.SetClipToScreen ();
         View.SetClipToScreen ();
         tf.Draw ();
         tf.Draw ();
+        View.SetClipToScreen ();
+        tf.PositionCursor ();
         DriverAssert.AssertDriverContentsAre ("fish", output);
         DriverAssert.AssertDriverContentsAre ("fish", output);
         Assert.Equal ("fi", tf.Text);
         Assert.Equal ("fi", tf.Text);
         Application.Top.Dispose ();
         Application.Top.Dispose ();
@@ -81,6 +84,7 @@ public class AppendAutocompleteTests (ITestOutputHelper output)
         Application.Driver?.SendKeys ('f', ConsoleKey.F, false, false, false);
         Application.Driver?.SendKeys ('f', ConsoleKey.F, false, false, false);
         View.SetClipToScreen ();
         View.SetClipToScreen ();
         tf.Draw ();
         tf.Draw ();
+        View.SetClipToScreen ();
         tf.PositionCursor ();
         tf.PositionCursor ();
         DriverAssert.AssertDriverContentsAre ("fish", output);
         DriverAssert.AssertDriverContentsAre ("fish", output);
         Assert.Equal ("f", tf.Text);
         Assert.Equal ("f", tf.Text);
@@ -90,6 +94,7 @@ public class AppendAutocompleteTests (ITestOutputHelper output)
 
 
         View.SetClipToScreen ();
         View.SetClipToScreen ();
         tf.Draw ();
         tf.Draw ();
+        View.SetClipToScreen ();
         tf.PositionCursor ();
         tf.PositionCursor ();
         DriverAssert.AssertDriverContentsAre ("friend", output);
         DriverAssert.AssertDriverContentsAre ("friend", output);
         Assert.Equal ("f", tf.Text);
         Assert.Equal ("f", tf.Text);
@@ -98,6 +103,7 @@ public class AppendAutocompleteTests (ITestOutputHelper output)
         Application.Driver?.SendKeys (' ', cycleKey, false, false, false);
         Application.Driver?.SendKeys (' ', cycleKey, false, false, false);
         View.SetClipToScreen ();
         View.SetClipToScreen ();
         tf.Draw ();
         tf.Draw ();
+        View.SetClipToScreen ();
         tf.PositionCursor ();
         tf.PositionCursor ();
         DriverAssert.AssertDriverContentsAre ("fish", output);
         DriverAssert.AssertDriverContentsAre ("fish", output);
         Assert.Equal ("f", tf.Text);
         Assert.Equal ("f", tf.Text);
@@ -114,6 +120,7 @@ public class AppendAutocompleteTests (ITestOutputHelper output)
         Application.Driver?.SendKeys ('f', ConsoleKey.F, false, false, false);
         Application.Driver?.SendKeys ('f', ConsoleKey.F, false, false, false);
         View.SetClipToScreen ();
         View.SetClipToScreen ();
         tf.Draw ();
         tf.Draw ();
+        View.SetClipToScreen ();
         tf.PositionCursor ();
         tf.PositionCursor ();
         DriverAssert.AssertDriverContentsAre ("fish", output);
         DriverAssert.AssertDriverContentsAre ("fish", output);
         Assert.Equal ("f", tf.Text);
         Assert.Equal ("f", tf.Text);
@@ -139,6 +146,7 @@ public class AppendAutocompleteTests (ITestOutputHelper output)
         Application.Driver?.SendKeys ('f', ConsoleKey.F, false, false, false);
         Application.Driver?.SendKeys ('f', ConsoleKey.F, false, false, false);
         View.SetClipToScreen ();
         View.SetClipToScreen ();
         tf.Draw ();
         tf.Draw ();
+        View.SetClipToScreen ();
         tf.PositionCursor ();
         tf.PositionCursor ();
         DriverAssert.AssertDriverContentsAre ("fish", output);
         DriverAssert.AssertDriverContentsAre ("fish", output);
         Assert.Equal ("f", tf.Text);
         Assert.Equal ("f", tf.Text);
@@ -160,10 +168,11 @@ public class AppendAutocompleteTests (ITestOutputHelper output)
 
 
         tf.Autocomplete = new AppendAutocomplete (tf);
         tf.Autocomplete = new AppendAutocomplete (tf);
         var generator = (SingleWordSuggestionGenerator)tf.Autocomplete.SuggestionGenerator;
         var generator = (SingleWordSuggestionGenerator)tf.Autocomplete.SuggestionGenerator;
-        generator.AllSuggestions = new List<string> { "FISH" };
+        generator.AllSuggestions = new() { "FISH" };
 
 
         View.SetClipToScreen ();
         View.SetClipToScreen ();
         tf.Draw ();
         tf.Draw ();
+        View.SetClipToScreen ();
         tf.PositionCursor ();
         tf.PositionCursor ();
         DriverAssert.AssertDriverContentsAre ("", output);
         DriverAssert.AssertDriverContentsAre ("", output);
         tf.NewKeyDownEvent (Key.M);
         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
         // Even though there is no match on case we should still get the suggestion
         View.SetClipToScreen ();
         View.SetClipToScreen ();
         tf.Draw ();
         tf.Draw ();
+        View.SetClipToScreen ();
         tf.PositionCursor ();
         tf.PositionCursor ();
         DriverAssert.AssertDriverContentsAre ("my fISH", output);
         DriverAssert.AssertDriverContentsAre ("my fISH", output);
         Assert.Equal ("my f", tf.Text);
         Assert.Equal ("my f", tf.Text);
@@ -196,17 +206,19 @@ public class AppendAutocompleteTests (ITestOutputHelper output)
 
 
         tf.Autocomplete = new AppendAutocomplete (tf);
         tf.Autocomplete = new AppendAutocomplete (tf);
         var generator = (SingleWordSuggestionGenerator)tf.Autocomplete.SuggestionGenerator;
         var generator = (SingleWordSuggestionGenerator)tf.Autocomplete.SuggestionGenerator;
-        generator.AllSuggestions = new List<string> { "fish" };
+        generator.AllSuggestions = new() { "fish" };
 
 
         View.SetClipToScreen ();
         View.SetClipToScreen ();
         tf.Draw ();
         tf.Draw ();
+        View.SetClipToScreen ();
         tf.PositionCursor ();
         tf.PositionCursor ();
         DriverAssert.AssertDriverContentsAre ("", output);
         DriverAssert.AssertDriverContentsAre ("", output);
 
 
-        tf.NewKeyDownEvent (new Key ('f'));
+        tf.NewKeyDownEvent (new ('f'));
 
 
         View.SetClipToScreen ();
         View.SetClipToScreen ();
         tf.Draw ();
         tf.Draw ();
+        View.SetClipToScreen ();
         tf.PositionCursor ();
         tf.PositionCursor ();
         DriverAssert.AssertDriverContentsAre ("fish", output);
         DriverAssert.AssertDriverContentsAre ("fish", output);
         Assert.Equal ("f", tf.Text);
         Assert.Equal ("f", tf.Text);
@@ -240,6 +252,7 @@ public class AppendAutocompleteTests (ITestOutputHelper output)
         Application.Driver?.SendKeys ('f', ConsoleKey.F, false, false, false);
         Application.Driver?.SendKeys ('f', ConsoleKey.F, false, false, false);
         View.SetClipToScreen ();
         View.SetClipToScreen ();
         tf.Draw ();
         tf.Draw ();
+        View.SetClipToScreen ();
         tf.PositionCursor ();
         tf.PositionCursor ();
         DriverAssert.AssertDriverContentsAre (expectRender, output);
         DriverAssert.AssertDriverContentsAre (expectRender, output);
         Assert.Equal ("f", tf.Text);
         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.Equal (Point.Empty, tv.CursorPosition);
         Assert.False (tv.ReadOnly);
         Assert.False (tv.ReadOnly);
         Assert.True (tv.CanFocus);
         Assert.True (tv.CanFocus);
+        Assert.False (tv.IsSelecting);
 
 
         var g = (SingleWordSuggestionGenerator)tv.Autocomplete.SuggestionGenerator;
         var g = (SingleWordSuggestionGenerator)tv.Autocomplete.SuggestionGenerator;
 
 
         tv.CanFocus = false;
         tv.CanFocus = false;
         Assert.True (tv.NewKeyDownEvent (Key.CursorLeft));
         Assert.True (tv.NewKeyDownEvent (Key.CursorLeft));
+        Assert.False (tv.IsSelecting);
         tv.CanFocus = true;
         tv.CanFocus = true;
         Assert.False (tv.NewKeyDownEvent (Key.CursorLeft));
         Assert.False (tv.NewKeyDownEvent (Key.CursorLeft));
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.CursorRight));
         Assert.True (tv.NewKeyDownEvent (Key.CursorRight));
         Assert.Equal (new (1, 0), tv.CursorPosition);
         Assert.Equal (new (1, 0), tv.CursorPosition);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.End.WithCtrl));
         Assert.True (tv.NewKeyDownEvent (Key.End.WithCtrl));
         Assert.Equal (2, tv.CurrentRow);
         Assert.Equal (2, tv.CurrentRow);
         Assert.Equal (23, tv.CurrentColumn);
         Assert.Equal (23, tv.CurrentColumn);
         Assert.Equal (tv.CurrentColumn, tv.GetCurrentLine ().Count);
         Assert.Equal (tv.CurrentColumn, tv.GetCurrentLine ().Count);
         Assert.Equal (new (23, 2), tv.CursorPosition);
         Assert.Equal (new (23, 2), tv.CursorPosition);
+        Assert.False (tv.IsSelecting);
         Assert.False (tv.NewKeyDownEvent (Key.CursorRight));
         Assert.False (tv.NewKeyDownEvent (Key.CursorRight));
         Assert.NotNull (tv.Autocomplete);
         Assert.NotNull (tv.Autocomplete);
         Assert.Empty (g.AllSuggestions);
         Assert.Empty (g.AllSuggestions);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.F.WithShift));
         Assert.True (tv.NewKeyDownEvent (Key.F.WithShift));
         tv.Draw ();
         tv.Draw ();
 
 
@@ -4931,6 +4937,7 @@ This is the second line.
                      );
                      );
         Assert.Equal (new (24, 2), tv.CursorPosition);
         Assert.Equal (new (24, 2), tv.CursorPosition);
         Assert.Empty (tv.Autocomplete.Suggestions);
         Assert.Empty (tv.Autocomplete.Suggestions);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.Z.WithCtrl));
         Assert.True (tv.NewKeyDownEvent (Key.Z.WithCtrl));
         tv.Draw ();
         tv.Draw ();
 
 
@@ -4940,6 +4947,7 @@ This is the second line.
                      );
                      );
         Assert.Equal (new (23, 2), tv.CursorPosition);
         Assert.Equal (new (23, 2), tv.CursorPosition);
         Assert.Empty (tv.Autocomplete.Suggestions);
         Assert.Empty (tv.Autocomplete.Suggestions);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.R.WithCtrl));
         Assert.True (tv.NewKeyDownEvent (Key.R.WithCtrl));
         tv.Draw ();
         tv.Draw ();
 
 
@@ -4949,6 +4957,7 @@ This is the second line.
                      );
                      );
         Assert.Equal (new (24, 2), tv.CursorPosition);
         Assert.Equal (new (24, 2), tv.CursorPosition);
         Assert.Empty (tv.Autocomplete.Suggestions);
         Assert.Empty (tv.Autocomplete.Suggestions);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.Backspace));
         Assert.True (tv.NewKeyDownEvent (Key.Backspace));
 
 
         Assert.Equal (
         Assert.Equal (
@@ -4969,6 +4978,7 @@ This is the second line.
         Assert.Equal ("line", g.AllSuggestions [4]);
         Assert.Equal ("line", g.AllSuggestions [4]);
         Assert.Equal ("second", g.AllSuggestions [5]);
         Assert.Equal ("second", g.AllSuggestions [5]);
         Assert.Equal ("third", g.AllSuggestions [^1]);
         Assert.Equal ("third", g.AllSuggestions [^1]);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.F.WithShift));
         Assert.True (tv.NewKeyDownEvent (Key.F.WithShift));
         tv.Draw ();
         tv.Draw ();
 
 
@@ -4979,6 +4989,7 @@ This is the second line.
         Assert.Equal (new (24, 2), tv.CursorPosition);
         Assert.Equal (new (24, 2), tv.CursorPosition);
         Assert.Single (tv.Autocomplete.Suggestions);
         Assert.Single (tv.Autocomplete.Suggestions);
         Assert.Equal ("first", tv.Autocomplete.Suggestions [0].Replacement);
         Assert.Equal ("first", tv.Autocomplete.Suggestions [0].Replacement);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.Enter));
         Assert.True (tv.NewKeyDownEvent (Key.Enter));
 
 
         Assert.Equal (
         Assert.Equal (
@@ -4992,68 +5003,85 @@ This is the second line.
         tv.Autocomplete.ClearSuggestions ();
         tv.Autocomplete.ClearSuggestions ();
         Assert.Empty (g.AllSuggestions);
         Assert.Empty (g.AllSuggestions);
         Assert.Empty (tv.Autocomplete.Suggestions);
         Assert.Empty (tv.Autocomplete.Suggestions);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.PageUp));
         Assert.True (tv.NewKeyDownEvent (Key.PageUp));
         Assert.Equal (24, tv.GetCurrentLine ().Count);
         Assert.Equal (24, tv.GetCurrentLine ().Count);
         Assert.Equal (new (24, 1), tv.CursorPosition);
         Assert.Equal (new (24, 1), tv.CursorPosition);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (new (Key.PageUp)));
         Assert.True (tv.NewKeyDownEvent (new (Key.PageUp)));
         Assert.Equal (23, tv.GetCurrentLine ().Count);
         Assert.Equal (23, tv.GetCurrentLine ().Count);
         Assert.Equal (new (23, 0), tv.CursorPosition);
         Assert.Equal (new (23, 0), tv.CursorPosition);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.PageDown));
         Assert.True (tv.NewKeyDownEvent (Key.PageDown));
         Assert.Equal (24, tv.GetCurrentLine ().Count);
         Assert.Equal (24, tv.GetCurrentLine ().Count);
         Assert.Equal (new (23, 1), tv.CursorPosition); // gets the previous length
         Assert.Equal (new (23, 1), tv.CursorPosition); // gets the previous length
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.V.WithCtrl));
         Assert.True (tv.NewKeyDownEvent (Key.V.WithCtrl));
         Assert.Equal (28, tv.GetCurrentLine ().Count);
         Assert.Equal (28, tv.GetCurrentLine ().Count);
         Assert.Equal (new (23, 2), tv.CursorPosition); // gets the previous length
         Assert.Equal (new (23, 2), tv.CursorPosition); // gets the previous length
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal ("", tv.SelectedText);
         Assert.Equal ("", tv.SelectedText);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.PageUp.WithShift));
         Assert.True (tv.NewKeyDownEvent (Key.PageUp.WithShift));
         Assert.Equal (24, tv.GetCurrentLine ().Count);
         Assert.Equal (24, tv.GetCurrentLine ().Count);
         Assert.Equal (new (23, 1), tv.CursorPosition); // gets the previous length
         Assert.Equal (new (23, 1), tv.CursorPosition); // gets the previous length
         Assert.Equal (24 + Environment.NewLine.Length, tv.SelectedLength);
         Assert.Equal (24 + Environment.NewLine.Length, tv.SelectedLength);
         Assert.Equal ($".{Environment.NewLine}This is the third line.", tv.SelectedText);
         Assert.Equal ($".{Environment.NewLine}This is the third line.", tv.SelectedText);
+        Assert.True (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.PageDown.WithShift));
         Assert.True (tv.NewKeyDownEvent (Key.PageDown.WithShift));
         Assert.Equal (28, tv.GetCurrentLine ().Count);
         Assert.Equal (28, tv.GetCurrentLine ().Count);
         Assert.Equal (new (23, 2), tv.CursorPosition); // gets the previous length
         Assert.Equal (new (23, 2), tv.CursorPosition); // gets the previous length
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal ("", tv.SelectedText);
         Assert.Equal ("", tv.SelectedText);
+        Assert.True (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.Home.WithCtrl));
         Assert.True (tv.NewKeyDownEvent (Key.Home.WithCtrl));
         Assert.Equal (Point.Empty, tv.CursorPosition);
         Assert.Equal (Point.Empty, tv.CursorPosition);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.N.WithCtrl));
         Assert.True (tv.NewKeyDownEvent (Key.N.WithCtrl));
         Assert.Equal (new (0, 1), tv.CursorPosition);
         Assert.Equal (new (0, 1), tv.CursorPosition);
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal ("", tv.SelectedText);
         Assert.Equal ("", tv.SelectedText);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.P.WithCtrl));
         Assert.True (tv.NewKeyDownEvent (Key.P.WithCtrl));
         Assert.Equal (Point.Empty, tv.CursorPosition);
         Assert.Equal (Point.Empty, tv.CursorPosition);
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal ("", tv.SelectedText);
         Assert.Equal ("", tv.SelectedText);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.CursorDown));
         Assert.True (tv.NewKeyDownEvent (Key.CursorDown));
         Assert.Equal (new (0, 1), tv.CursorPosition);
         Assert.Equal (new (0, 1), tv.CursorPosition);
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal ("", tv.SelectedText);
         Assert.Equal ("", tv.SelectedText);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.CursorUp));
         Assert.True (tv.NewKeyDownEvent (Key.CursorUp));
         Assert.Equal (Point.Empty, tv.CursorPosition);
         Assert.Equal (Point.Empty, tv.CursorPosition);
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal ("", tv.SelectedText);
         Assert.Equal ("", tv.SelectedText);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.CursorDown.WithShift));
         Assert.True (tv.NewKeyDownEvent (Key.CursorDown.WithShift));
         Assert.Equal (new (0, 1), tv.CursorPosition);
         Assert.Equal (new (0, 1), tv.CursorPosition);
         Assert.Equal (23 + Environment.NewLine.Length, tv.SelectedLength);
         Assert.Equal (23 + Environment.NewLine.Length, tv.SelectedLength);
         Assert.Equal ($"This is the first line.{Environment.NewLine}", tv.SelectedText);
         Assert.Equal ($"This is the first line.{Environment.NewLine}", tv.SelectedText);
+        Assert.True (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.CursorUp.WithShift));
         Assert.True (tv.NewKeyDownEvent (Key.CursorUp.WithShift));
         Assert.Equal (Point.Empty, tv.CursorPosition);
         Assert.Equal (Point.Empty, tv.CursorPosition);
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal ("", tv.SelectedText);
         Assert.Equal ("", tv.SelectedText);
+        Assert.True (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.F.WithCtrl));
         Assert.True (tv.NewKeyDownEvent (Key.F.WithCtrl));
         Assert.Equal (new (1, 0), tv.CursorPosition);
         Assert.Equal (new (1, 0), tv.CursorPosition);
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal ("", tv.SelectedText);
         Assert.Equal ("", tv.SelectedText);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.B.WithCtrl));
         Assert.True (tv.NewKeyDownEvent (Key.B.WithCtrl));
         Assert.Equal (Point.Empty, tv.CursorPosition);
         Assert.Equal (Point.Empty, tv.CursorPosition);
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal ("", tv.SelectedText);
         Assert.Equal ("", tv.SelectedText);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.CursorRight));
         Assert.True (tv.NewKeyDownEvent (Key.CursorRight));
         Assert.Equal (new (1, 0), tv.CursorPosition);
         Assert.Equal (new (1, 0), tv.CursorPosition);
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal ("", tv.SelectedText);
         Assert.Equal ("", tv.SelectedText);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.CursorLeft));
         Assert.True (tv.NewKeyDownEvent (Key.CursorLeft));
         Assert.Equal (Point.Empty, tv.CursorPosition);
         Assert.Equal (Point.Empty, tv.CursorPosition);
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal (0, tv.SelectedLength);
@@ -5103,6 +5131,7 @@ This is the second line.
                       tv.Text
                       tv.Text
                      );
                      );
         Assert.Equal (new (21, 0), tv.CursorPosition);
         Assert.Equal (new (21, 0), tv.CursorPosition);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.Backspace));
         Assert.True (tv.NewKeyDownEvent (Key.Backspace));
 
 
         Assert.Equal (
         Assert.Equal (
@@ -5110,6 +5139,7 @@ This is the second line.
                       tv.Text
                       tv.Text
                      );
                      );
         Assert.Equal (new (20, 0), tv.CursorPosition);
         Assert.Equal (new (20, 0), tv.CursorPosition);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.Backspace));
         Assert.True (tv.NewKeyDownEvent (Key.Backspace));
 
 
         Assert.Equal (
         Assert.Equal (
@@ -5117,6 +5147,7 @@ This is the second line.
                       tv.Text
                       tv.Text
                      );
                      );
         Assert.Equal (new (19, 0), tv.CursorPosition);
         Assert.Equal (new (19, 0), tv.CursorPosition);
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.Home));
         Assert.True (tv.NewKeyDownEvent (Key.Home));
         Assert.Equal (Point.Empty, tv.CursorPosition);
         Assert.Equal (Point.Empty, tv.CursorPosition);
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal (0, tv.SelectedLength);
@@ -5421,7 +5452,6 @@ This is the second line.
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal (0, tv.SelectedLength);
         Assert.Equal ("", tv.SelectedText);
         Assert.Equal ("", tv.SelectedText);
         Assert.False (tv.IsSelecting);
         Assert.False (tv.IsSelecting);
-        Assert.False (tv.IsSelecting);
         Assert.True (tv.NewKeyDownEvent (Key.Backspace.WithCtrl));
         Assert.True (tv.NewKeyDownEvent (Key.Backspace.WithCtrl));
         Assert.Equal ($"This is the second line.{Environment.NewLine}This is the third ", tv.Text);
         Assert.Equal ($"This is the second line.{Environment.NewLine}This is the third ", tv.Text);
         Assert.Equal (new (18, 1), tv.CursorPosition);
         Assert.Equal (new (18, 1), tv.CursorPosition);
@@ -5493,6 +5523,7 @@ This is the second line.
         Assert.False (tv.Used);
         Assert.False (tv.Used);
         Assert.True (tv.AllowsTab);
         Assert.True (tv.AllowsTab);
         Assert.Equal (new (18, 2), tv.CursorPosition);
         Assert.Equal (new (18, 2), tv.CursorPosition);
+        Assert.True (tv.IsSelecting);
         tv.AllowsTab = false;
         tv.AllowsTab = false;
         Assert.False (tv.NewKeyDownEvent (Key.Tab));
         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",
                       $"{Environment.NewLine}This is the second line.{Environment.NewLine}This is the third \t",
                       tv.Text
                       tv.Text
                      );
                      );
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.AllowsTab);
         Assert.True (tv.AllowsTab);
         tv.AllowsTab = false;
         tv.AllowsTab = false;
         Assert.False (tv.NewKeyDownEvent (Key.Tab.WithShift));
         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",
                       $"{Environment.NewLine}This is the second line.{Environment.NewLine}This is the third \t",
                       tv.Text
                       tv.Text
                      );
                      );
+        Assert.False (tv.IsSelecting);
         Assert.False (tv.AllowsTab);
         Assert.False (tv.AllowsTab);
         tv.AllowsTab = true;
         tv.AllowsTab = true;
         Assert.True (tv.NewKeyDownEvent (Key.Tab.WithShift));
         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 ",
                       $"{Environment.NewLine}This is the second line.{Environment.NewLine}This is the third ",
                       tv.Text
                       tv.Text
                      );
                      );
+        Assert.False (tv.IsSelecting);
         Assert.True (tv.AllowsTab);
         Assert.True (tv.AllowsTab);
         Assert.False (tv.NewKeyDownEvent (Key.F6));
         Assert.False (tv.NewKeyDownEvent (Key.F6));
         Assert.False (tv.NewKeyDownEvent (Application.NextTabGroupKey));
         Assert.False (tv.NewKeyDownEvent (Application.NextTabGroupKey));
@@ -5535,6 +5569,7 @@ This is the second line.
 
 
         Assert.True (tv.NewKeyDownEvent (PopoverMenu.DefaultKey));
         Assert.True (tv.NewKeyDownEvent (PopoverMenu.DefaultKey));
         Assert.True (tv.ContextMenu != null && tv.ContextMenu.Visible);
         Assert.True (tv.ContextMenu != null && tv.ContextMenu.Visible);
+        Assert.False (tv.IsSelecting);
         top.Dispose ();
         top.Dispose ();
     }
     }
 
 
@@ -7105,7 +7140,7 @@ line.
         Assert.True (tv.NewMouseEvent (new () { Position = new (0, 3), Flags = MouseFlags.Button1Pressed }));
         Assert.True (tv.NewMouseEvent (new () { Position = new (0, 3), Flags = MouseFlags.Button1Pressed }));
         tv.Draw ();
         tv.Draw ();
         Assert.Equal (new (0, 3), tv.CursorPosition);
         Assert.Equal (new (0, 3), tv.CursorPosition);
-        Assert.Equal (new (13, 0), cp);
+        Assert.Equal (new (12, 0), cp);
 
 
         DriverAssert.AssertDriverContentsWithFrameAre (
         DriverAssert.AssertDriverContentsWithFrameAre (
                                                        @"
                                                        @"

+ 0 - 1
Tests/UnitTests/Views/ToplevelTests.cs

@@ -743,7 +743,6 @@ public class ToplevelTests
     }
     }
 
 
     [Fact]
     [Fact]
-    [TestRespondersDisposed]
     public void Multi_Thread_Toplevels ()
     public void Multi_Thread_Toplevels ()
     {
     {
         Application.Init (new FakeDriver ());
         Application.Init (new FakeDriver ());

+ 0 - 119
Tests/UnitTestsParallelizable/Configuration/DeepClonerTests.cs

@@ -56,11 +56,6 @@ public class DeepClonerTests
         public bool Immutable { get; init; }
         public bool Immutable { get; init; }
     }
     }
 
 
-    private class SettingsScopeMock : Dictionary<string, ConfigPropertyMock>
-    {
-        public string? Theme { get; set; }
-    }
-
     private class ComplexKey
     private class ComplexKey
     {
     {
         public int Id { get; init; }
         public int Id { get; init; }
@@ -544,120 +539,6 @@ public class DeepClonerTests
         Assert.Equal (source.Immutable, result.Immutable);
         Assert.Equal (source.Immutable, result.Immutable);
     }
     }
 
 
-    [Fact]
-    public void SettingsScopeMockWithKey_CreatesDeepCopy ()
-    {
-        SettingsScopeMock? source = new ()
-        {
-            Theme = "Dark",
-            ["KeyBinding"] = new () { PropertyValue = new Key (KeyCode.A) { Handled = true } },
-            ["Counts"] = new () { PropertyValue = new Dictionary<string, int> { { "X", 1 } } }
-        };
-        SettingsScopeMock? result = DeepCloner.DeepClone (source);
-
-        Assert.NotNull (result);
-        Assert.NotSame (source, result);
-        Assert.Equal (source.Theme, result!.Theme);
-        Assert.NotSame (source ["KeyBinding"], result ["KeyBinding"]);
-        Assert.NotSame (source ["Counts"], result ["Counts"]);
-
-        ConfigPropertyMock clonedKeyProp = result ["KeyBinding"];
-        var clonedKey = (Key)clonedKeyProp.PropertyValue!;
-        Assert.NotSame (source ["KeyBinding"].PropertyValue, clonedKey);
-        Assert.Equal (((Key)source ["KeyBinding"].PropertyValue!).KeyCode, clonedKey.KeyCode);
-        Assert.Equal (((Key)source ["KeyBinding"].PropertyValue!).Handled, clonedKey.Handled);
-
-        Assert.Equal ((Dictionary<string, int>)source ["Counts"].PropertyValue!, (Dictionary<string, int>)result ["Counts"].PropertyValue!);
-
-        // Modify result, ensure source unchanged
-        result.Theme = "Light";
-        clonedKey.Handled = false;
-        ((Dictionary<string, int>)result ["Counts"].PropertyValue!).Add ("Y", 2);
-        Assert.Equal ("Dark", source.Theme);
-        Assert.True (((Key)source ["KeyBinding"].PropertyValue!).Handled);
-        Assert.Single ((Dictionary<string, int>)source ["Counts"].PropertyValue!);
-    }
-
-    [Fact]
-    public void ThemeScopeList_WithThemes_ClonesSuccessfully ()
-    {
-        // Arrange: Create a ThemeScope and verify a property exists
-        var defaultThemeScope = new ThemeScope ();
-        defaultThemeScope.LoadHardCodedDefaults ();
-        Assert.True (defaultThemeScope.ContainsKey ("Button.DefaultHighlightStyle"));
-
-        var darkThemeScope = new ThemeScope ();
-        darkThemeScope.LoadHardCodedDefaults ();
-        Assert.True (darkThemeScope.ContainsKey ("Button.DefaultHighlightStyle"));
-
-        // Create a Themes list with two themes
-        List<Dictionary<string, ThemeScope>> themesList =
-        [
-            new () { { "Default", defaultThemeScope } },
-            new () { { "Dark", darkThemeScope } }
-        ];
-
-        // Create a SettingsScope and set the Themes property
-        var settingsScope = new SettingsScope ();
-        settingsScope.LoadHardCodedDefaults ();
-        Assert.True (settingsScope.ContainsKey ("Themes"));
-        settingsScope ["Themes"].PropertyValue = themesList;
-
-        // Act
-        SettingsScope? result = DeepCloner.DeepClone (settingsScope);
-
-        // Assert
-        Assert.NotNull (result);
-        Assert.IsType<SettingsScope> (result);
-        var resultScope = (SettingsScope)result;
-        Assert.True (resultScope.ContainsKey ("Themes"));
-
-        Assert.NotNull (resultScope ["Themes"].PropertyValue);
-
-        List<Dictionary<string, ThemeScope>> clonedThemes = (List<Dictionary<string, ThemeScope>>)resultScope ["Themes"].PropertyValue!;
-        Assert.Equal (2, clonedThemes.Count);
-    }
-
-    [Fact]
-    public void Empty_SettingsScope_ClonesSuccessfully ()
-    {
-        // Arrange: Create a SettingsScope 
-        var settingsScope = new SettingsScope ();
-        Assert.True (settingsScope.ContainsKey ("Themes"));
-
-        // Act
-        SettingsScope? result = DeepCloner.DeepClone (settingsScope);
-
-        // Assert
-        Assert.NotNull (result);
-        Assert.IsType<SettingsScope> (result);
-
-        Assert.True (result.ContainsKey ("Themes"));
-    }
-
-    [Fact]
-    public void SettingsScope_With_Themes_Set_ClonesSuccessfully ()
-    {
-        // Arrange: Create a SettingsScope 
-        var settingsScope = new SettingsScope ();
-        Assert.True (settingsScope.ContainsKey ("Themes"));
-
-        settingsScope ["Themes"].PropertyValue = new List<Dictionary<string, ThemeScope>>
-        {
-            new() { { "Default", new () } },
-            new() { { "Dark", new () } }
-        };
-
-        // Act
-        SettingsScope? result = DeepCloner.DeepClone (settingsScope);
-
-        // Assert
-        Assert.NotNull (result);
-        Assert.IsType<SettingsScope> (result);
-        Assert.True (result.ContainsKey ("Themes"));
-        Assert.NotNull (result ["Themes"].PropertyValue);
-    }
-
     [Fact]
     [Fact]
     public void LargeObject_PerformsWithinLimit ()
     public void LargeObject_PerformsWithinLimit ()
     {
     {

+ 0 - 1
Tests/UnitTestsParallelizable/Configuration/SourcesManagerTests.cs

@@ -1,6 +1,5 @@
 using System.Reflection;
 using System.Reflection;
 using System.Text.Json;
 using System.Text.Json;
-using Terminal.Gui.Configuration;
 
 
 public class SourcesManagerTests
 public class SourcesManagerTests
 {
 {

+ 1 - 1
docfx/docs/drawing.md

@@ -63,7 +63,7 @@ The @Terminal.Gui.Application MainLoop will iterate over all Views in the view h
 12) DrawComplete is raised.
 12) DrawComplete is raised.
 13) The current View's Frame NOT INCLUDING the Margin is excluded from the current Clip region.
 13) The current View's Frame NOT INCLUDING the Margin is excluded from the current Clip region.
 
 
-Most of the steps above can be overridden by developers using the standard [Terminal.Gui Cancellable Work Pattern](cancellable_work_pattern.md). For example, the base @Terminal.Gui.View always clears the viewport. To override this, a subclass can override @Terminal.Gui.View.OnClearingViewport to simply return `true`. Or, a user of `View` can subscribe to the @Terminal.Gui.View.ClearingViewport event and set the `Cancel` argument to `true`.
+Most of the steps above can be overridden by developers using the standard [Terminal.Gui Cancellable Work Pattern](cancellable-work-pattern.md). For example, the base @Terminal.Gui.View always clears the viewport. To override this, a subclass can override @Terminal.Gui.View.OnClearingViewport to simply return `true`. Or, a user of `View` can subscribe to the @Terminal.Gui.View.ClearingViewport event and set the `Cancel` argument to `true`.
 
 
 Then, after the above steps have completed, the Mainloop will iterate through all views in the view hierarchy again, this time calling Draw on any @Terminal.Gui.View.Margin objects, using the cached Clip region mentioned above. This enables Margin to be transparent.
 Then, after the above steps have completed, the Mainloop will iterate through all views in the view hierarchy again, this time calling Draw on any @Terminal.Gui.View.Margin objects, using the cached Clip region mentioned above. This enables Margin to be transparent.
 
 

+ 1 - 1
docfx/docs/events.md

@@ -4,7 +4,7 @@ Terminal.Gui exposes and uses events in many places. This deep dive covers the p
 
 
 ## See Also
 ## See Also
 
 
-* [Cancellable Work Pattern](cancellable_work_pattern.md)
+* [Cancellable Work Pattern](cancellable-work-pattern.md)
 * [Command Deep Dive](command.md)
 * [Command Deep Dive](command.md)
 
 
 ## Tenets for Terminal.Gui Events (Unless you know better ones...)
 ## Tenets for Terminal.Gui Events (Unless you know better ones...)

+ 1 - 1
docfx/docs/index.md

@@ -21,7 +21,7 @@ See [What's New in V2 For more](newinv2.md).
 ## Conceptual Documentation
 ## Conceptual Documentation
 
 
 * [Arrangement API](arrangement.md)
 * [Arrangement API](arrangement.md)
-* [Cancellable Work Pattern](cancellable_work_pattern.md)
+* [Cancellable Work Pattern](cancellable-work-pattern.md)
 * [Configuration and Theme Manager](config.md)
 * [Configuration and Theme Manager](config.md)
 * [Command Deep Dive](command.md)
 * [Command Deep Dive](command.md)
 * [Cursor Deep Dive](cursor.md)
 * [Cursor Deep Dive](cursor.md)

+ 1 - 1
docfx/docs/keyboard.md

@@ -2,7 +2,7 @@
 
 
 ## See Also
 ## See Also
 
 
-* [Cancellable Work Pattern](cancellable_work_pattern.md)
+* [Cancellable Work Pattern](cancellable-work-pattern.md)
 * [Command Deep Dive](command.md)
 * [Command Deep Dive](command.md)
 
 
 ## Tenets for Terminal.Gui Keyboard Handling (Unless you know better ones...)
 ## Tenets for Terminal.Gui Keyboard Handling (Unless you know better ones...)

+ 1 - 1
docfx/docs/mouse.md

@@ -2,7 +2,7 @@
 
 
 ## See Also
 ## See Also
 
 
-* [Cancellable Work Pattern](cancellable_work_pattern.md)
+* [Cancellable Work Pattern](cancellable-work-pattern.md)
 * [Command Deep Dive](command.md)
 * [Command Deep Dive](command.md)
 
 
 
 

+ 23 - 0
docfx/docs/newinv2.md

@@ -28,6 +28,8 @@ The entire library has been reviewed and simplified. As a result, the API is mor
 * *New!* @Terminal.Gui.PosAlign - Aligns a set of views horizontally or vertically (left, right, center, etc...).
 * *New!* @Terminal.Gui.PosAlign - Aligns a set of views horizontally or vertically (left, right, center, etc...).
 * *New!* @Terminal.Gui.View.Arrangement enables tiled and overlapped view arrangement and moving/resizing Views with the keyboard and mouse. See [Arrangement](arrangement.md).
 * *New!* @Terminal.Gui.View.Arrangement enables tiled and overlapped view arrangement and moving/resizing Views with the keyboard and mouse. See [Arrangement](arrangement.md).
 * *Improved!* Keyboard [Navigation](navigation.md) has been revamped to be more reliability and ensure TUI apps built with Terminal.Gui are accessible. 
 * *Improved!* Keyboard [Navigation](navigation.md) has been revamped to be more reliability and ensure TUI apps built with Terminal.Gui are accessible. 
+* *New!* Sizable/Movable views - Any view can now be set to have resizeable borders and/or be dragged around.
+* *Improved!* Consistent tabbing behavior - Tab navigation now behaves as expected, cleanly and consistently.
 
 
 ## New and Improved Built-in Views
 ## New and Improved Built-in Views
 
 
@@ -41,12 +43,30 @@ The entire library has been reviewed and simplified. As a result, the API is mor
 * *[MenuBar](~/api/Terminal.Gui.MenuBar.yml)* - COMING SOON! New implementation based on `Bar`
 * *[MenuBar](~/api/Terminal.Gui.MenuBar.yml)* - COMING SOON! New implementation based on `Bar`
 * *[ContextMenu](~/api/Terminal.Gui.ContextMenu.yml)* - COMING SOON! New implementation based on `Bar`
 * *[ContextMenu](~/api/Terminal.Gui.ContextMenu.yml)* - COMING SOON! New implementation based on `Bar`
 * *[FileDialog](~/api/Terminal.Gui.FileDialog.yml)* - The new, modern file dialog includes icons (in TUI!) for files/folders, search, and a `TreeView`. 
 * *[FileDialog](~/api/Terminal.Gui.FileDialog.yml)* - The new, modern file dialog includes icons (in TUI!) for files/folders, search, and a `TreeView`. 
+* *[TableView](tableview.md)* - No longer just DataTable, now supports any collections, checkboxes and even expandable trees
 * *@"Terminal.Gui.ColorPicker"* - Fully supports TrueColor with the ability to choose a color using HSV, RGB, or HSL as well as W3C standard color names.
 * *@"Terminal.Gui.ColorPicker"* - Fully supports TrueColor with the ability to choose a color using HSV, RGB, or HSL as well as W3C standard color names.
 
 
+## Beauty
+
+Terminal.Gui has never been prettier
+
+* *ShowBorders* - Get that 3D 'pop' for your buttons
+* *Gradient* - Render beautiful true color borders, titles etc with the new Gradient API
+
+
 ## Configuration Manager
 ## Configuration Manager
 
 
 Terminal.Gui now supports a configuration manager enabling library and app settings to be persisted and loaded from the file system. See [Configuration Manager](https://gui-cs.github.io/Terminal.GuiV2Docs/docs/overview.html#configuration-manager) for details.
 Terminal.Gui now supports a configuration manager enabling library and app settings to be persisted and loaded from the file system. See [Configuration Manager](https://gui-cs.github.io/Terminal.GuiV2Docs/docs/overview.html#configuration-manager) for details.
 
 
+## Logging & Metrics
+
+Terminal.Gui now features multi level logging of engine internals and system performance metrics (redraws, invoke durations etc).  Never again wonder why your frame rate is low, or a given terminal/distro does not behave as expected.
+See [Logging](logging.md) for details.
+
+## Sixel Image Support
+
+Recently added to Windows Terminal and long supported in mainstream linux terminals, this graphics protcol allows images and even animations to be rendered directly into the console.
+
 ## Updated Keyboard API
 ## Updated Keyboard API
 
 
 The API for handling keyboard input is significantly improved. See [Keyboard API](keyboard.md).
 The API for handling keyboard input is significantly improved. See [Keyboard API](keyboard.md).
@@ -63,3 +83,6 @@ The API for mouse input is now internally consistent and easiser to use.
 * More granular APIs are provided to ease handling specific mouse actions. See [Mouse API](mouse.md).
 * More granular APIs are provided to ease handling specific mouse actions. See [Mouse API](mouse.md).
 * Views can use the `View.Highlight` event to have the view be visibly highlighted on various mouse events.
 * Views can use the `View.Highlight` event to have the view be visibly highlighted on various mouse events.
 * Views can set `View.WantContinousButtonPresses = true` to ahve their `Command.Accept` command be invoked repeatedly as the user holds a mouse button down on the view.
 * Views can set `View.WantContinousButtonPresses = true` to ahve their `Command.Accept` command be invoked repeatedly as the user holds a mouse button down on the view.
+
+## AOT support
+*AOT/single file app support* now works out of the box.

+ 1 - 1
docfx/docs/toc.yml

@@ -9,7 +9,7 @@
 - name: Arrangement
 - name: Arrangement
   href: arrangement.md
   href: arrangement.md
 - name: Cancellable Work Pattern
 - name: Cancellable Work Pattern
-  href: cancellable_work_pattern.md
+  href: cancellable-work-pattern.md
 - name: Configuration
 - name: Configuration
   href: config.md
   href: config.md
 - name: Command Deep Dive
 - name: Command Deep Dive

BIN
local_packages/Terminal.Gui.2.0.0.nupkg


BIN
local_packages/Terminal.Gui.2.0.0.snupkg