diff --git a/Examples/UICatalog/Scenarios/VkeyPacketSimulator.cs b/Examples/UICatalog/Scenarios/VkeyPacketSimulator.cs index 9b7024056..8c88163d4 100644 --- a/Examples/UICatalog/Scenarios/VkeyPacketSimulator.cs +++ b/Examples/UICatalog/Scenarios/VkeyPacketSimulator.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Terminal.Gui; -using Terminal.Gui.ConsoleDrivers; namespace UICatalog.Scenarios; diff --git a/Terminal.Gui/Application/Application.Run.cs b/Terminal.Gui/Application/Application.Run.cs index a3798e28f..585b94d1d 100644 --- a/Terminal.Gui/Application/Application.Run.cs +++ b/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) @@ -588,25 +588,23 @@ public static partial class Application // Run (Begin, Run, End, Stop) // End the RunState.Toplevel // 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 // 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"); } - - TopLevels.Pop (); } // Notify that it is closing 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 }) diff --git a/Terminal.Gui/Application/Application.Toplevel.cs b/Terminal.Gui/Application/Application.Toplevel.cs index 3403d1eda..1b7a58d94 100644 --- a/Terminal.Gui/Application/Application.Toplevel.cs +++ b/Terminal.Gui/Application/Application.Toplevel.cs @@ -1,12 +1,26 @@ #nullable enable +using System.Collections.Concurrent; + namespace Terminal.Gui; 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 + private static readonly ConcurrentStack _topLevels = new (); + private static readonly object _topLevelsLock = new (); + /// Holds the stack of TopLevel views. - internal static Stack TopLevels { get; } = new (); + internal static ConcurrentStack TopLevels + { + get + { + lock (_topLevelsLock) + { + return _topLevels; + } + } + } /// The that is currently active. /// The top. diff --git a/Terminal.Gui/Configuration/ConfigurationManager.cs b/Terminal.Gui/Configuration/ConfigurationManager.cs index 23d7311ea..0ef9482e7 100644 --- a/Terminal.Gui/Configuration/ConfigurationManager.cs +++ b/Terminal.Gui/Configuration/ConfigurationManager.cs @@ -8,7 +8,6 @@ using System.Reflection; using System.Text.Encodings.Web; using System.Text.Json; using System.Text.Json.Serialization; -using Terminal.Gui.Configuration; namespace Terminal.Gui; @@ -150,7 +149,7 @@ public static class ConfigurationManager private static ImmutableSortedDictionary? _uninitializedConfigPropertiesCache; #pragma warning disable IDE1006 // Naming Styles - private static readonly object __uninitializedConfigPropertiesCacheCacheLock = new (); + private static readonly object _uninitializedConfigPropertiesCacheCacheLock = new (); #pragma warning restore IDE1006 // Naming Styles /// @@ -181,7 +180,7 @@ public static class ConfigurationManager ConfigProperty.Initialize (); // Cache all configuration properties - lock (__uninitializedConfigPropertiesCacheCacheLock) + lock (_uninitializedConfigPropertiesCacheCacheLock) { // _allConfigProperties: for ordered, iterable access (LINQ-friendly) // _frozenConfigPropertyCache: for high-speed key lookup (frozen) @@ -777,15 +776,17 @@ public static class ConfigurationManager return _uninitializedConfigPropertiesCache; } - // Filter properties by scope using the cached ScopeType property instead of reflection - IEnumerable>? filtered = _uninitializedConfigPropertiesCache?.Where (cp => cp.Value.ScopeType == scopeType); + lock (_uninitializedConfigPropertiesCacheCacheLock) + { + // Filter properties by scope using the cached ScopeType property instead of reflection + IEnumerable>? filtered = _uninitializedConfigPropertiesCache?.Where (cp => cp.Value.ScopeType == scopeType); - Debug.Assert (filtered is { }); + Debug.Assert (filtered is { }); - IEnumerable> configPropertiesByScope = filtered as KeyValuePair [] ?? filtered.ToArray (); - Debug.Assert (configPropertiesByScope.All (v => !v.Value.HasValue)); - - return configPropertiesByScope; + IEnumerable> configPropertiesByScope = filtered as KeyValuePair [] ?? filtered.ToArray (); + Debug.Assert (configPropertiesByScope.All (v => !v.Value.HasValue)); + return configPropertiesByScope; + } } /// diff --git a/Terminal.Gui/Configuration/DeepCloner.cs b/Terminal.Gui/Configuration/DeepCloner.cs index 70e3a8d7f..d91ad663b 100644 --- a/Terminal.Gui/Configuration/DeepCloner.cs +++ b/Terminal.Gui/Configuration/DeepCloner.cs @@ -285,21 +285,32 @@ public static class DeepCloner IDictionary tempDict = CreateDictionaryInstance (dictType, comparer); 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!)) + object? lastKey = null; + try + { + // Clone all key-value pairs + foreach (object? key in sourceDict.Keys) { - tempDict [clonedKey!] = clonedValue; - } - else - { - 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 (type.GetConstructor (Type.EmptyTypes) != null) diff --git a/Terminal.Gui/Configuration/SourcesManager.cs b/Terminal.Gui/Configuration/SourcesManager.cs index 729086541..83a412c6e 100644 --- a/Terminal.Gui/Configuration/SourcesManager.cs +++ b/Terminal.Gui/Configuration/SourcesManager.cs @@ -4,7 +4,7 @@ using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Text.Json; -namespace Terminal.Gui.Configuration; +namespace Terminal.Gui; /// /// Manages the Sources and provides the API for loading them. Source is a location where a configuration can be stored. Sources are defined in . diff --git a/Terminal.Gui/Configuration/ThemeManager.cs b/Terminal.Gui/Configuration/ThemeManager.cs index 599b86519..b9a3aefa4 100644 --- a/Terminal.Gui/Configuration/ThemeManager.cs +++ b/Terminal.Gui/Configuration/ThemeManager.cs @@ -101,6 +101,10 @@ public static class ThemeManager /// 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 + /// /// Gets the Themes dictionary. is preferred. /// The backing store is ["Themes"]. diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleKeyMapping.cs b/Terminal.Gui/ConsoleDrivers/ConsoleKeyMapping.cs index 6fce2e040..a57efa5be 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleKeyMapping.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleKeyMapping.cs @@ -1,7 +1,7 @@ using System.Globalization; 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? /// Helper class to handle the scan code and virtual key from a . diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 971fc5de2..bdca5ac79 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -4,7 +4,6 @@ // using System.Runtime.InteropServices; -using Terminal.Gui.ConsoleDrivers; using Unix.Terminal; namespace Terminal.Gui; diff --git a/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs b/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs index f3e025004..8bd54b275 100644 --- a/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs +++ b/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs @@ -1,6 +1,5 @@ #nullable enable using System.Globalization; -using static Terminal.Gui.ConsoleDrivers.ConsoleKeyMapping; namespace Terminal.Gui; @@ -1159,7 +1158,7 @@ public static class EscSeqUtils break; default: - uint ck = MapKeyCodeToConsoleKey ((KeyCode)consoleKeyInfo.KeyChar, out bool isConsoleKey); + uint ck = ConsoleKeyMapping.MapKeyCodeToConsoleKey ((KeyCode)consoleKeyInfo.KeyChar, out bool isConsoleKey); if (isConsoleKey) { @@ -1415,12 +1414,12 @@ public static class EscSeqUtils if (keyInfo.Modifiers != ConsoleModifiers.Shift) { // 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. "Ç") // 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 @@ -1431,14 +1430,14 @@ public static class EscSeqUtils 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) if (keyInfo.Key != ConsoleKey.None && 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) @@ -1469,7 +1468,7 @@ public static class EscSeqUtils || keyInfo.Modifiers.HasFlag (ConsoleModifiers.Control)) { // 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) @@ -1484,7 +1483,7 @@ public static class EscSeqUtils return (KeyCode)keyInfo.Key; } - return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)keyInfo.KeyChar); + return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)keyInfo.KeyChar); } private static async Task ProcessButtonClickedAsync () diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs index 575d5f206..cea3367ec 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs @@ -2,8 +2,6 @@ // FakeConsole.cs: A fake .NET Windows Console API implementation for unit tests. // -using Terminal.Gui.ConsoleDrivers; - namespace Terminal.Gui; #pragma warning disable RCS1138 // Add summary to documentation comment. diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index 3dc008242..5c4872ae4 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -4,7 +4,6 @@ using System.Diagnostics; using System.Runtime.InteropServices; -using Terminal.Gui.ConsoleDrivers; // Alias Console to MockConsole so we don't accidentally use Console diff --git a/Terminal.Gui/ConsoleDrivers/V2/WindowsKeyConverter.cs b/Terminal.Gui/ConsoleDrivers/V2/WindowsKeyConverter.cs index fd092db67..c74e10ca7 100644 --- a/Terminal.Gui/ConsoleDrivers/V2/WindowsKeyConverter.cs +++ b/Terminal.Gui/ConsoleDrivers/V2/WindowsKeyConverter.cs @@ -1,5 +1,4 @@ #nullable enable -using Terminal.Gui.ConsoleDrivers; namespace Terminal.Gui; diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver/WindowsConsole.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver/WindowsConsole.cs index 3dfd993aa..90c5aa412 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver/WindowsConsole.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver/WindowsConsole.cs @@ -2,7 +2,6 @@ using System.Collections.Concurrent; using System.ComponentModel; using System.Runtime.InteropServices; -using Terminal.Gui.ConsoleDrivers; namespace Terminal.Gui; diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver/WindowsDriver.cs index b10349994..302e71883 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver/WindowsDriver.cs @@ -19,8 +19,6 @@ using System.ComponentModel; using System.Diagnostics; using System.Runtime.InteropServices; -using static Terminal.Gui.ConsoleDrivers.ConsoleKeyMapping; -using static Terminal.Gui.SpinnerStyle; namespace Terminal.Gui; @@ -76,7 +74,7 @@ internal class WindowsDriver : ConsoleDriver public static WindowsConsole.KeyEventRecord FromVKPacketToKeyEventRecord (WindowsConsole.KeyEventRecord keyEvent) { - if (keyEvent.wVirtualKeyCode != (VK)ConsoleKey.Packet) + if (keyEvent.wVirtualKeyCode != (ConsoleKeyMapping.VK)ConsoleKey.Packet) { return keyEvent; } @@ -106,8 +104,8 @@ internal class WindowsDriver : ConsoleDriver mod.HasFlag (ConsoleModifiers.Shift), mod.HasFlag (ConsoleModifiers.Alt), mod.HasFlag (ConsoleModifiers.Control)); - cKeyInfo = DecodeVKPacketToKConsoleKeyInfo (cKeyInfo); - uint scanCode = GetScanCodeFromConsoleKeyInfo (cKeyInfo); + cKeyInfo = ConsoleKeyMapping.DecodeVKPacketToKConsoleKeyInfo (cKeyInfo); + uint scanCode = ConsoleKeyMapping.GetScanCodeFromConsoleKeyInfo (cKeyInfo); return new WindowsConsole.KeyEventRecord { @@ -115,7 +113,7 @@ internal class WindowsDriver : ConsoleDriver bKeyDown = keyEvent.bKeyDown, dwControlKeyState = keyEvent.dwControlKeyState, wRepeatCount = keyEvent.wRepeatCount, - wVirtualKeyCode = (VK)cKeyInfo.Key, + wVirtualKeyCode = (ConsoleKeyMapping.VK)cKeyInfo.Key, wVirtualScanCode = (ushort)scanCode }; } @@ -139,7 +137,7 @@ internal class WindowsDriver : ConsoleDriver { controlKey |= WindowsConsole.ControlKeyState.ShiftPressed; keyEvent.UnicodeChar = '\0'; - keyEvent.wVirtualKeyCode = VK.SHIFT; + keyEvent.wVirtualKeyCode = ConsoleKeyMapping.VK.SHIFT; } if (alt) @@ -147,7 +145,7 @@ internal class WindowsDriver : ConsoleDriver controlKey |= WindowsConsole.ControlKeyState.LeftAltPressed; controlKey |= WindowsConsole.ControlKeyState.RightAltPressed; keyEvent.UnicodeChar = '\0'; - keyEvent.wVirtualKeyCode = VK.MENU; + keyEvent.wVirtualKeyCode = ConsoleKeyMapping.VK.MENU; } if (control) @@ -155,7 +153,7 @@ internal class WindowsDriver : ConsoleDriver controlKey |= WindowsConsole.ControlKeyState.LeftControlPressed; controlKey |= WindowsConsole.ControlKeyState.RightControlPressed; keyEvent.UnicodeChar = '\0'; - keyEvent.wVirtualKeyCode = VK.CONTROL; + keyEvent.wVirtualKeyCode = ConsoleKeyMapping.VK.CONTROL; } keyEvent.dwControlKeyState = controlKey; @@ -174,7 +172,7 @@ internal class WindowsDriver : ConsoleDriver //} else { // keyEvent.wVirtualKeyCode = '\0'; //} - keyEvent.wVirtualKeyCode = (VK)key; + keyEvent.wVirtualKeyCode = (ConsoleKeyMapping.VK)key; input.KeyEvent = keyEvent; @@ -492,7 +490,7 @@ internal class WindowsDriver : ConsoleDriver switch (inputEvent.EventType) { 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. // The VK_PACKET key is the low word of a 32-bit @@ -681,7 +679,7 @@ internal class WindowsDriver : ConsoleDriver case ConsoleKey.OemMinus: // 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. - uint mapResult = MapVKtoChar ((VK)keyInfo.Key); + uint mapResult = ConsoleKeyMapping.MapVKtoChar ((ConsoleKeyMapping.VK)keyInfo.Key); 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 // we should keep it - return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)mappedChar); + return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)mappedChar); } // KeyChar is printable @@ -751,12 +749,12 @@ internal class WindowsDriver : ConsoleDriver if (keyInfo.Modifiers != ConsoleModifiers.Shift) { // 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. "�") // 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: @@ -772,13 +770,13 @@ internal class WindowsDriver : ConsoleDriver // AltGr support - AltGr is equivalent to Ctrl+Alt 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)) { - return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)(uint)keyInfo.Key); + return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)(uint)keyInfo.Key); } if ((keyInfo.Modifiers == ConsoleModifiers.Shift) ^ keyInfoEx.CapsLock) @@ -810,47 +808,47 @@ internal class WindowsDriver : ConsoleDriver if (Enum.IsDefined (typeof (KeyCode), (uint)keyInfo.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 return KeyCode.ShiftMask; } - if (keyInfo.Key == (ConsoleKey)VK.CONTROL) + if (keyInfo.Key == (ConsoleKey)ConsoleKeyMapping.VK.CONTROL) { // Ctrl 17 return KeyCode.CtrlMask; } - if (keyInfo.Key == (ConsoleKey)VK.MENU) + if (keyInfo.Key == (ConsoleKey)ConsoleKeyMapping.VK.MENU) { // Alt 18 return KeyCode.AltMask; } 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 MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)keyInfo.Key); + return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)keyInfo.Key); } 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) 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) diff --git a/Terminal.Gui/View/View.Layout.cs b/Terminal.Gui/View/View.Layout.cs index 1fe4f4636..14d9a8828 100644 --- a/Terminal.Gui/View/View.Layout.cs +++ b/Terminal.Gui/View/View.Layout.cs @@ -800,14 +800,6 @@ public partial class View // Layout APIs SuperView?.SetNeedsLayout (); } - if (SuperView is null) - { - foreach (Toplevel tl in Application.TopLevels) - { - // tl.SetNeedsDraw (); - } - } - if (this is not Adornment adornment) { return; diff --git a/Tests/TerminalGuiFluentTesting/GuiTestContext.cs b/Tests/TerminalGuiFluentTesting/GuiTestContext.cs index 29083ffab..4442c4f54 100644 --- a/Tests/TerminalGuiFluentTesting/GuiTestContext.cs +++ b/Tests/TerminalGuiFluentTesting/GuiTestContext.cs @@ -2,7 +2,6 @@ using System.Text; using Microsoft.Extensions.Logging; using Terminal.Gui; -using Terminal.Gui.ConsoleDrivers; namespace TerminalGuiFluentTesting; diff --git a/Tests/UnitTests/Configuration/ConfigurationMangerTests.cs b/Tests/UnitTests/Configuration/ConfigurationMangerTests.cs index eb00652f0..4d3d1f18d 100644 --- a/Tests/UnitTests/Configuration/ConfigurationMangerTests.cs +++ b/Tests/UnitTests/Configuration/ConfigurationMangerTests.cs @@ -414,7 +414,7 @@ public class ConfigurationManagerTests (ITestOutputHelper output) """; // 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 Load (ConfigLocations.Runtime); @@ -447,7 +447,7 @@ public class ConfigurationManagerTests (ITestOutputHelper output) ResetToHardCodedDefaults (); // Serialize to a JSON string - string json = SourcesManager?.ToJson (Settings); + string json = ConfigurationManager.SourcesManager?.ToJson (Settings); // Write the JSON string to the file File.WriteAllText ("hard_coded_defaults_config.json", json); @@ -476,7 +476,7 @@ public class ConfigurationManagerTests (ITestOutputHelper output) Enable (ConfigLocations.LibraryResources); // Serialize to a JSON string - string json = SourcesManager?.ToJson (Settings); + string json = ConfigurationManager.SourcesManager?.ToJson (Settings); // Write the JSON string to the file 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 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 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); @@ -773,7 +773,7 @@ public class ConfigurationManagerTests (ITestOutputHelper output) ] }"; - var jsonException = Assert.Throws (() => SourcesManager?.Load (Settings, json, "test", ConfigLocations.Runtime)); + var jsonException = Assert.Throws (() => ConfigurationManager.SourcesManager?.Load (Settings, json, "test", ConfigLocations.Runtime)); Assert.StartsWith ("foreground: \"\"brownish\"\"", jsonException.Message); // AbNormal is not a Scheme attribute @@ -797,7 +797,7 @@ public class ConfigurationManagerTests (ITestOutputHelper output) ] }"; - jsonException = Assert.Throws (() => SourcesManager?.Load (Settings, json, "test", ConfigLocations.Runtime)); + jsonException = Assert.Throws (() => ConfigurationManager.SourcesManager?.Load (Settings, json, "test", ConfigLocations.Runtime)); Assert.StartsWith ("AbNormal:", jsonException.Message); // Modify hotNormal background only @@ -820,7 +820,7 @@ public class ConfigurationManagerTests (ITestOutputHelper output) ] }"; - jsonException = Assert.Throws (() => SourcesManager?.Load (Settings, json, "test", ConfigLocations.Runtime)); + jsonException = Assert.Throws (() => ConfigurationManager.SourcesManager?.Load (Settings, json, "test", ConfigLocations.Runtime)); Assert.StartsWith ("background:", jsonException.Message); // Unknown property @@ -829,7 +829,7 @@ public class ConfigurationManagerTests (ITestOutputHelper output) ""Unknown"" : ""Not known"" }"; - jsonException = Assert.Throws (() => SourcesManager?.Load (Settings, json, "test", ConfigLocations.Runtime)); + jsonException = Assert.Throws (() => ConfigurationManager.SourcesManager?.Load (Settings, json, "test", ConfigLocations.Runtime)); Assert.StartsWith ("Unknown:", jsonException.Message); Assert.Equal (0, _jsonErrors.Length); @@ -989,7 +989,7 @@ public class ConfigurationManagerTests (ITestOutputHelper output) ResetToCurrentValues (); ThrowOnJsonErrors = true; - SourcesManager?.Load (Settings, json, "UpdateFromJson", ConfigLocations.Runtime); + ConfigurationManager.SourcesManager?.Load (Settings, json, "UpdateFromJson", ConfigLocations.Runtime); Assert.Equal ("Default", ThemeManager.Theme); diff --git a/Tests/UnitTests/Configuration/SettingsScopeTests.cs b/Tests/UnitTests/Configuration/SettingsScopeTests.cs index e70e0682d..33b1c4bf2 100644 --- a/Tests/UnitTests/Configuration/SettingsScopeTests.cs +++ b/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; namespace Terminal.Gui.ConfigurationTests; @@ -12,7 +12,7 @@ public class SettingsScopeTests // arrange Enable (ConfigLocations.HardCoded); - Assert.Equal (Key.Esc, (Key)Settings! ["Application.QuitKey"].PropertyValue); + Assert.Equal (Key.Esc, (Key)Settings! ["Application.QuitKey"].PropertyValue!); ThrowOnJsonErrors = true; @@ -27,7 +27,7 @@ public class SettingsScopeTests Load (ConfigLocations.Runtime); // assert - Assert.Equal (Key.Q.WithCtrl, (Key)Settings ["Application.QuitKey"].PropertyValue); + Assert.Equal (Key.Q.WithCtrl, (Key)Settings ["Application.QuitKey"].PropertyValue!); // clean up Disable (resetToHardCodedDefaults: true); @@ -43,11 +43,11 @@ public class SettingsScopeTests ThrowOnJsonErrors = true; ConfigProperty themesConfigProperty = Settings! ["Themes"]; - ConcurrentDictionary dict = themesConfigProperty.PropertyValue as ConcurrentDictionary; + ConcurrentDictionary dict = (themesConfigProperty.PropertyValue as ConcurrentDictionary)!; Assert.NotNull (dict); Assert.Single (dict); - Assert.NotEmpty ((ConcurrentDictionary)themesConfigProperty.PropertyValue); + Assert.NotEmpty (((ConcurrentDictionary)themesConfigProperty.PropertyValue!)!); ThemeScope scope = dict [ThemeManager.DEFAULT_THEME_NAME]; Assert.NotNull (scope); @@ -111,16 +111,16 @@ public class SettingsScopeTests Load (ConfigLocations.LibraryResources); // arrange - Assert.Equal (Key.Esc, (Key)Settings! ["Application.QuitKey"].PropertyValue); + Assert.Equal (Key.Esc, (Key)Settings!["Application.QuitKey"].PropertyValue!); Assert.Equal ( Key.F6, - (Key)Settings ["Application.NextTabGroupKey"].PropertyValue + (Key)Settings["Application.NextTabGroupKey"].PropertyValue! ); Assert.Equal ( Key.F6.WithShift, - (Key)Settings ["Application.PrevTabGroupKey"].PropertyValue + (Key)Settings["Application.PrevTabGroupKey"].PropertyValue! ); // act @@ -154,9 +154,9 @@ public class SettingsScopeTests updatedSettings ["Application.PrevTabGroupKey"].PropertyValue = Key.B; 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); } @@ -218,4 +218,134 @@ public class SettingsScopeTests Disable (resetToHardCodedDefaults: true); } + + private class ConfigPropertyMock + { + public object? PropertyValue { get; init; } + public bool Immutable { get; init; } + } + + private class SettingsScopeMock : Dictionary + { + 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 { { "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)source ["Counts"].PropertyValue!, (Dictionary)result ["Counts"].PropertyValue!); + + // Modify result, ensure source unchanged + result.Theme = "Light"; + clonedKey.Handled = false; + ((Dictionary)result ["Counts"].PropertyValue!).Add ("Y", 2); + Assert.Equal ("Dark", source.Theme); + Assert.True (((Key)source ["KeyBinding"].PropertyValue!).Handled); + Assert.Single ((Dictionary)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> 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 (result); + SettingsScope resultScope = (SettingsScope)result; + Assert.True (resultScope.ContainsKey ("Themes")); + + Assert.NotNull (resultScope ["Themes"].PropertyValue); + + List> clonedThemes = (List>)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 (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> + { + new() { { "Default", new () } }, + new() { { "Dark", new () } } + }; + + // Act + SettingsScope? result = DeepCloner.DeepClone (settingsScope); + + // Assert + Assert.NotNull (result); + Assert.IsType (result); + Assert.True (result.ContainsKey ("Themes")); + Assert.NotNull (result ["Themes"].PropertyValue); + Disable (resetToHardCodedDefaults: true); + } } diff --git a/Tests/UnitTests/Configuration/ThemeManagerTests.cs b/Tests/UnitTests/Configuration/ThemeManagerTests.cs index 071022208..1bb6b46f6 100644 --- a/Tests/UnitTests/Configuration/ThemeManagerTests.cs +++ b/Tests/UnitTests/Configuration/ThemeManagerTests.cs @@ -272,7 +272,7 @@ public class ThemeManagerTests (ITestOutputHelper output) 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 output.WriteLine ($"JSON size: {json.Length / 1024} Kb"); diff --git a/Tests/UnitTests/ConsoleDrivers/ConsoleKeyMappingTests.cs b/Tests/UnitTests/ConsoleDrivers/ConsoleKeyMappingTests.cs index 59b0bd8c2..f2a58a9b4 100644 --- a/Tests/UnitTests/ConsoleDrivers/ConsoleKeyMappingTests.cs +++ b/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 { @@ -365,7 +364,7 @@ public class ConsoleKeyMappingTests ) { var consoleKeyInfo = new ConsoleKeyInfo (keyChar, consoleKey, shift, alt, control); - KeyCode keyCode = MapConsoleKeyInfoToKeyCode (consoleKeyInfo); + KeyCode keyCode = ConsoleKeyMapping.MapConsoleKeyInfoToKeyCode (consoleKeyInfo); Assert.Equal (keyCode, expectedKeyCode); } @@ -395,9 +394,9 @@ public class ConsoleKeyMappingTests KeyCode expectedKeyCode ) { - ConsoleModifiers modifiers = GetModifiers (shift, alt, control); + ConsoleModifiers modifiers = ConsoleKeyMapping.GetModifiers (shift, alt, control); var keyCode = (KeyCode)keyChar; - keyCode = MapToKeyCodeModifiers (modifiers, keyCode); + keyCode = ConsoleKeyMapping.MapToKeyCodeModifiers (modifiers, keyCode); Assert.Equal (keyCode, expectedKeyCode); } @@ -414,7 +413,7 @@ public class ConsoleKeyMappingTests ) { var consoleKeyInfo = new ConsoleKeyInfo (keyChar, consoleKey, shift, alt, control); - uint scanCode = GetScanCodeFromConsoleKeyInfo (consoleKeyInfo); + uint scanCode = ConsoleKeyMapping.GetScanCodeFromConsoleKeyInfo (consoleKeyInfo); Assert.Equal (scanCode, expectedScanCode); } @@ -492,12 +491,12 @@ public class ConsoleKeyMappingTests 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); var keyCode = (KeyCode)keyChar; - keyCode = MapToKeyCodeModifiers (modifiers, keyCode); + keyCode = ConsoleKeyMapping.MapToKeyCodeModifiers (modifiers, keyCode); Assert.Equal (keyCode, expectedKeyCode); } @@ -541,12 +540,12 @@ public class ConsoleKeyMappingTests 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); var keyCode = (KeyCode)keyChar; - keyCode = MapToKeyCodeModifiers (modifiers, keyCode); + keyCode = ConsoleKeyMapping.MapToKeyCodeModifiers (modifiers, keyCode); Assert.Equal (keyCode, expectedKeyCode); } diff --git a/Tests/UnitTests/ConsoleDrivers/V2/WindowsInputProcessorTests.cs b/Tests/UnitTests/ConsoleDrivers/V2/WindowsInputProcessorTests.cs index ab723ee8d..2d9f74950 100644 --- a/Tests/UnitTests/ConsoleDrivers/V2/WindowsInputProcessorTests.cs +++ b/Tests/UnitTests/ConsoleDrivers/V2/WindowsInputProcessorTests.cs @@ -1,5 +1,4 @@ using System.Collections.Concurrent; -using Terminal.Gui.ConsoleDrivers; using InputRecord = Terminal.Gui.WindowsConsole.InputRecord; using ButtonState = Terminal.Gui.WindowsConsole.ButtonState; using EventFlags = Terminal.Gui.WindowsConsole.EventFlags; diff --git a/Tests/UnitTests/Views/ToplevelTests.cs b/Tests/UnitTests/Views/ToplevelTests.cs index 082ffb1f3..862ae44f7 100644 --- a/Tests/UnitTests/Views/ToplevelTests.cs +++ b/Tests/UnitTests/Views/ToplevelTests.cs @@ -743,7 +743,6 @@ public class ToplevelTests } [Fact] - [TestRespondersDisposed] public void Multi_Thread_Toplevels () { Application.Init (new FakeDriver ()); diff --git a/Tests/UnitTestsParallelizable/Configuration/DeepClonerTests.cs b/Tests/UnitTestsParallelizable/Configuration/DeepClonerTests.cs index 010019e60..91901ed3d 100644 --- a/Tests/UnitTestsParallelizable/Configuration/DeepClonerTests.cs +++ b/Tests/UnitTestsParallelizable/Configuration/DeepClonerTests.cs @@ -56,11 +56,6 @@ public class DeepClonerTests public bool Immutable { get; init; } } - private class SettingsScopeMock : Dictionary - { - public string? Theme { get; set; } - } - private class ComplexKey { public int Id { get; init; } @@ -544,120 +539,6 @@ public class DeepClonerTests 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 { { "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)source ["Counts"].PropertyValue!, (Dictionary)result ["Counts"].PropertyValue!); - - // Modify result, ensure source unchanged - result.Theme = "Light"; - clonedKey.Handled = false; - ((Dictionary)result ["Counts"].PropertyValue!).Add ("Y", 2); - Assert.Equal ("Dark", source.Theme); - Assert.True (((Key)source ["KeyBinding"].PropertyValue!).Handled); - Assert.Single ((Dictionary)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> 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 (result); - var resultScope = (SettingsScope)result; - Assert.True (resultScope.ContainsKey ("Themes")); - - Assert.NotNull (resultScope ["Themes"].PropertyValue); - - List> clonedThemes = (List>)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 (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> - { - new() { { "Default", new () } }, - new() { { "Dark", new () } } - }; - - // Act - SettingsScope? result = DeepCloner.DeepClone (settingsScope); - - // Assert - Assert.NotNull (result); - Assert.IsType (result); - Assert.True (result.ContainsKey ("Themes")); - Assert.NotNull (result ["Themes"].PropertyValue); - } - [Fact] public void LargeObject_PerformsWithinLimit () { diff --git a/Tests/UnitTestsParallelizable/Configuration/SourcesManagerTests.cs b/Tests/UnitTestsParallelizable/Configuration/SourcesManagerTests.cs index 464ee17e7..a27169e38 100644 --- a/Tests/UnitTestsParallelizable/Configuration/SourcesManagerTests.cs +++ b/Tests/UnitTestsParallelizable/Configuration/SourcesManagerTests.cs @@ -1,6 +1,5 @@ using System.Reflection; using System.Text.Json; -using Terminal.Gui.Configuration; public class SourcesManagerTests { diff --git a/local_packages/Terminal.Gui.2.0.0.nupkg b/local_packages/Terminal.Gui.2.0.0.nupkg index d0d27c8a8..096bd81ef 100644 Binary files a/local_packages/Terminal.Gui.2.0.0.nupkg and b/local_packages/Terminal.Gui.2.0.0.nupkg differ diff --git a/local_packages/Terminal.Gui.2.0.0.snupkg b/local_packages/Terminal.Gui.2.0.0.snupkg index 15b94e60c..51d1a9ca2 100644 Binary files a/local_packages/Terminal.Gui.2.0.0.snupkg and b/local_packages/Terminal.Gui.2.0.0.snupkg differ