diff --git a/Terminal.Gui/Configuration/ConfigurationManager.cs b/Terminal.Gui/Configuration/ConfigurationManager.cs index 5e05ecfb42..75b024612f 100644 --- a/Terminal.Gui/Configuration/ConfigurationManager.cs +++ b/Terminal.Gui/Configuration/ConfigurationManager.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Reflection; using System.Text; +using System.Text.Encodings.Web; using System.Text.Json; using System.Text.Json.Serialization; using static Terminal.Gui.SpinnerStyle; @@ -70,8 +71,13 @@ public static partial class ConfigurationManager { // We override the standard Rune converter to support specifying Glyphs in // a flexible way new RuneJsonConverter(), + // Override Key to support "Ctrl+Q" format. + new KeyJsonConverter() }, - }; + // Enables Key to be "Ctrl+Q" vs "Ctrl\u002BQ" + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping + +}; /// /// A dictionary of all properties in the Terminal.Gui project that are decorated with the attribute. diff --git a/Terminal.Gui/Configuration/KeyJsonConverter.cs b/Terminal.Gui/Configuration/KeyJsonConverter.cs index e75d5fcb22..d9c17996e6 100644 --- a/Terminal.Gui/Configuration/KeyJsonConverter.cs +++ b/Terminal.Gui/Configuration/KeyJsonConverter.cs @@ -3,46 +3,12 @@ using System.Text.Json.Serialization; namespace Terminal.Gui; -class KeyJsonConverter : JsonConverter { - - public override Key Read (ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - if (reader.TokenType == JsonTokenType.StartObject) { - Key key = Key.Empty; - while (reader.Read ()) { - if (reader.TokenType == JsonTokenType.EndObject) { - break; - } - if (reader.TokenType == JsonTokenType.PropertyName) { - string propertyName = reader.GetString (); - reader.Read (); +/// +/// Support for in JSON in the form of "Ctrl-X" or "Alt-Shift-F1". +/// +public class KeyJsonConverter : JsonConverter { + public override Key Read (ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => Key.TryParse (reader.GetString (), out var key) ? key : Key.Empty; - switch (propertyName?.ToLowerInvariant ()) { - case "key": - if (reader.TokenType == JsonTokenType.String) { - string keyValue = reader.GetString (); - if (Key.TryParse (keyValue, out key)) { - break; - } - throw new JsonException ($"Error parsing Key: {keyValue}."); - - } - break; - default: - throw new JsonException ($"Unexpected Key property \"{propertyName}\"."); - } - } - } - return key; - } - throw new JsonException ($"Unexpected StartObject token when parsing Key: {reader.TokenType}."); - } - - public override void Write (Utf8JsonWriter writer, Key value, JsonSerializerOptions options) - { - writer.WriteStartObject (); - writer.WriteString ("Key", value.ToString ()); - writer.WriteEndObject (); - } -} + public override void Write (Utf8JsonWriter writer, Key value, JsonSerializerOptions options) => writer.WriteStringValue (value.ToString ()); +} \ No newline at end of file diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index b337234094..0b5bc9cc62 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -649,7 +649,6 @@ public enum CursorVisibility { /// Lowercase alpha keys are encoded as values between 65 and 90 corresponding to the un-shifted A to Z keys on a keyboard. Enum values /// are provided for these (e.g. , , etc.). Even though the values are the same as the ASCII /// values for uppercase characters, these enum values represent *lowercase*, un-shifted characters. -/// TODO: Strongly consider renaming these from .A to .Z to .A_Lowercase to .Z_Lowercase (or .a to .z). /// /// /// Numeric keys are the values between 48 and 57 corresponding to 0 to 9 (e.g. , , etc.). @@ -680,7 +679,7 @@ public enum KeyCode : uint { /// /// If the is set, then the value is that of the special mask, - /// otherwise, the value is the one of the lower bits (as extracted by ). + /// otherwise, the value is in the the lower bits (as extracted by ). /// SpecialMask = 0xfff00000, @@ -791,111 +790,111 @@ public enum KeyCode : uint { D9, /// - /// The key code for the user pressing Shift-A + /// The key code for the A key /// A = 65, /// - /// The key code for the user pressing Shift-B + /// The key code for the B key /// B, /// - /// The key code for the user pressing Shift-C + /// The key code for the C key /// C, /// - /// The key code for the user pressing Shift-D + /// The key code for the D key /// D, /// - /// The key code for the user pressing Shift-E + /// The key code for the E key /// E, /// - /// The key code for the user pressing Shift-F + /// The key code for the F key /// F, /// - /// The key code for the user pressing Shift-G + /// The key code for the G key /// G, /// - /// The key code for the user pressing Shift-H + /// The key code for the H key /// H, /// - /// The key code for the user pressing Shift-I + /// The key code for the I key /// I, /// - /// The key code for the user pressing Shift-J + /// The key code for the J key /// J, /// - /// The key code for the user pressing Shift-K + /// The key code for the K key /// K, /// - /// The key code for the user pressing Shift-L + /// The key code for the L key /// L, /// - /// The key code for the user pressing Shift-M + /// The key code for the M key /// M, /// - /// The key code for the user pressing Shift-N + /// The key code for the N key /// N, /// - /// The key code for the user pressing Shift-O + /// The key code for the O key /// O, /// - /// The key code for the user pressing Shift-P + /// The key code for the P key /// P, /// - /// The key code for the user pressing Shift-Q + /// The key code for the Q key /// Q, /// - /// The key code for the user pressing Shift-R + /// The key code for the R key /// R, /// - /// The key code for the user pressing Shift-S + /// The key code for the S key /// S, /// - /// The key code for the user pressing Shift-T + /// The key code for the T key /// T, /// - /// The key code for the user pressing Shift-U + /// The key code for the U key /// U, /// - /// The key code for the user pressing Shift-V + /// The key code for the V key /// V, /// - /// The key code for the user pressing Shift-W + /// The key code for the W key /// W, /// - /// The key code for the user pressing Shift-X + /// The key code for the X key /// X, /// - /// The key code for the user pressing Shift-Y + /// The key code for the Y key /// Y, /// - /// The key code for the user pressing Shift-Z + /// The key code for the Z key /// Z, /// - /// The key code for the user pressing A + /// The key code for the Delete key. /// Delete = 127, diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index cda6e1359b..f9915d7952 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1051,16 +1051,17 @@ KeyCode MapKey (WindowsConsole.ConsoleKeyInfoEx keyInfoEx) return (KeyCode)((uint)KeyCode.F1 + delta); } + // If the key is JUST a modifier, return it as that key if (key == (ConsoleKey)16) { // Shift - return KeyCode.Null | KeyCode.ShiftMask; + return KeyCode.ShiftKey; } if (key == (ConsoleKey)17) { // Ctrl - return KeyCode.Null | KeyCode.CtrlMask; + return KeyCode.CtrlKey; } if (key == (ConsoleKey)18) { // Alt - return KeyCode.Null | KeyCode.AltMask; + return KeyCode.AltKey; } return ConsoleKeyMapping.MapKeyModifiers (keyInfo, (KeyCode)((uint)keyInfo.KeyChar)); diff --git a/Terminal.Gui/Input/Key.cs b/Terminal.Gui/Input/Key.cs index 8c23a8d5c2..c3498931b8 100644 --- a/Terminal.Gui/Input/Key.cs +++ b/Terminal.Gui/Input/Key.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using System.Text.Json.Serialization; +using static System.Runtime.CompilerServices.RuntimeHelpers; namespace Terminal.Gui; @@ -64,7 +65,6 @@ namespace Terminal.Gui; /// /// /// -[JsonConverter (typeof (KeyJsonConverter))] public class Key : EventArgs, IEquatable { /// /// Constructs a new @@ -72,11 +72,56 @@ public class Key : EventArgs, IEquatable { public Key () : this (KeyCode.Null) { } /// - /// Constructs a new from the provided Key value + /// Constructs a new from the provided Key value /// /// The key public Key (KeyCode k) => KeyCode = k; + /// + /// Constructs a new from a char. + /// + /// + /// + /// The key codes for the A..Z keys are encoded as values between 65 and 90 ( through ). + /// While these are the same as the ASCII values for uppercase characters, they represent *keys*, not characters. + /// Therefore, this constructor will store 'A'..'Z' as .. with the + /// set and will + /// store `a`..`z` as ... + /// + /// + /// + public Key (char ch) + { + switch (ch) { + case >= 'A' and <= 'Z': + // Upper case A..Z mean "Shift-char" so we need to add Shift + KeyCode = (KeyCode)ch | KeyCode.ShiftMask; + break; + case >= 'a' and <= 'z': + // Lower case a..z mean no shift, so we need to store as Key.A...Key.Z + KeyCode = (KeyCode)(ch - 32); + return; + default: + KeyCode = (KeyCode)ch; + break; + } + } + + /// + /// Constructs a new Key from a string describing the key. + /// See for information + /// on the format of the string. + /// + /// The string describing the key. + public Key (string str) + { + var result = Key.TryParse (str, out Key key); + if (!result) { + throw new ArgumentException (@$"Invalid key string: {str}", nameof (str)); + } + KeyCode = key.KeyCode; + } + /// /// Indicates if the current Key event has already been processed and the driver should stop notifying any other event subscriber. /// Its important to set this value to true specially when updating any View's layout from inside the subscriber method. @@ -94,7 +139,6 @@ public Key () : this (KeyCode.Null) { } /// /// This property is the backing data for the . It is a enum value. /// - [JsonInclude] [JsonConverter (typeof (KeyCodeJsonConverter))] public KeyCode KeyCode { get; init; } /// @@ -106,8 +150,11 @@ [JsonInclude] [JsonConverter (typeof (KeyCodeJsonConverter))] /// The key value as a Rune. This is the actual value of the key pressed, and is independent of the modifiers. /// /// - /// If the key pressed is a letter (a-z or A-Z), this will be the upper or lower case letter depending on whether the shift key is pressed. - /// If the key is outside of the range, this will be . + /// + /// If the key is a letter (a-z or A-Z), the Rune be the upper or lower case letter depending on whether + /// the shift key was pressed. + /// If the key is outside of the range, the returned Rune will be . + /// /// public Rune AsRune => ToRune (KeyCode); @@ -115,11 +162,14 @@ [JsonInclude] [JsonConverter (typeof (KeyCodeJsonConverter))] /// Converts a to a . /// /// - /// If the key is a letter (a-z or A-Z), this will be the upper or lower case letter depending on whether the shift key is pressed. - /// If the key is outside of the range, this will be . + /// + /// If the key is a letter (a-z or A-Z), the Rune be the upper or lower case letter depending on whether + /// the shift key was pressed. + /// If the key is outside of the range, the returned Rune will be . + /// /// /// - /// The key converted to a rune. if conversion is not possible. + /// The key converted to a Rune. if conversion is not possible. public static Rune ToRune (KeyCode key) { if (key is KeyCode.Null or KeyCode.SpecialMask || key.HasFlag (KeyCode.CtrlMask) || key.HasFlag (KeyCode.AltMask)) { @@ -277,14 +327,34 @@ public static bool GetIsKeyCodeAtoZ (KeyCode keyCode) /// Cast to a . /// /// - public static implicit operator Key (KeyCode keyCode) => new (keyCode); - + public static implicit operator Key (KeyCode keyCode) => new Key (keyCode); /// /// Cast to a . /// + /// + /// See for more information. + /// /// - public static implicit operator Key (char ch) => new ((KeyCode)ch); + public static implicit operator Key (char ch) => new Key (ch); + + /// + /// Cast to a . + /// + /// + /// See for more information. + /// + /// + public static implicit operator Key (string str) => new Key (str); + + /// + /// Cast a to a . + /// + /// + /// See for more information. + /// + /// + public static implicit operator string (Key key) => key.ToString (); /// public override bool Equals (object obj) => obj is Key k && k.KeyCode == KeyCode; @@ -563,416 +633,416 @@ public static bool TryParse (string text, [NotNullWhen (true)] out Key key) /// /// An uninitialized The object. /// - public new static readonly Key Empty = new (); + public new static Key Empty => new (); /// /// The object for the Backspace key. /// - public static readonly Key Backspace = new (KeyCode.Backspace); + public static Key Backspace => new (KeyCode.Backspace); /// /// The object for the tab key (forwards tab key). /// - public static readonly Key Tab = new (KeyCode.Tab); + public static Key Tab => new (KeyCode.Tab); /// /// The object for the return key. /// - public static readonly Key Enter = new (KeyCode.Enter); + public static Key Enter => new (KeyCode.Enter); /// /// The object for the clear key. /// - public static readonly Key Clear = new (KeyCode.Clear); + public static Key Clear => new (KeyCode.Clear); /// /// The object for the Shift key. /// - public static readonly Key Shift = new (KeyCode.ShiftKey); + public static Key Shift => new (KeyCode.ShiftKey); /// /// The object for the Ctrl key. /// - public static readonly Key Ctrl = new (KeyCode.CtrlKey); + public static Key Ctrl => new (KeyCode.CtrlKey); /// /// The object for the Alt key. /// - public static readonly Key Alt = new (KeyCode.AltKey); + public static Key Alt => new (KeyCode.AltKey); /// /// The object for the CapsLock key. /// - public static readonly Key CapsLock = new (KeyCode.CapsLock); + public static Key CapsLock => new (KeyCode.CapsLock); /// /// The object for the Escape key. /// - public static readonly Key Esc = new (KeyCode.Esc); + public static Key Esc => new (KeyCode.Esc); /// /// The object for the Space bar key. /// - public static readonly Key Space = new (KeyCode.Space); + public static Key Space => new (KeyCode.Space); /// /// The object for 0 key. /// - public static readonly Key D0 = new (KeyCode.D0); + public static Key D0 => new (KeyCode.D0); /// /// The object for 1 key. /// - public static readonly Key D1 = new (KeyCode.D1); + public static Key D1 => new (KeyCode.D1); /// /// The object for 2 key. /// - public static readonly Key D2 = new (KeyCode.D2); + public static Key D2 => new (KeyCode.D2); /// /// The object for 3 key. /// - public static readonly Key D3 = new (KeyCode.D3); + public static Key D3 => new (KeyCode.D3); /// /// The object for 4 key. /// - public static readonly Key D4 = new (KeyCode.D4); + public static Key D4 => new (KeyCode.D4); /// /// The object for 5 key. /// - public static readonly Key D5 = new (KeyCode.D5); + public static Key D5 => new (KeyCode.D5); /// /// The object for 6 key. /// - public static readonly Key D6 = new (KeyCode.D6); + public static Key D6 => new (KeyCode.D6); /// /// The object for 7 key. /// - public static readonly Key D7 = new (KeyCode.D7); + public static Key D7 => new (KeyCode.D7); /// /// The object for 8 key. /// - public static readonly Key D8 = new (KeyCode.D8); + public static Key D8 => new (KeyCode.D8); /// /// The object for 9 key. /// - public static readonly Key D9 = new (KeyCode.D9); + public static Key D9 => new (KeyCode.D9); /// /// The object for the A key (un-shifted). Use Key.A.WithShift for uppercase 'A'. /// - public static readonly Key A = new (KeyCode.A); + public static Key A => new (KeyCode.A); /// /// The object for the B key (un-shifted). Use Key.B.WithShift for uppercase 'B'. /// - public static readonly Key B = new (KeyCode.B); + public static Key B => new (KeyCode.B); /// /// The object for the C key (un-shifted). Use Key.C.WithShift for uppercase 'C'. /// - public static readonly Key C = new (KeyCode.C); + public static Key C => new (KeyCode.C); /// /// The object for the D key (un-shifted). Use Key.D.WithShift for uppercase 'D'. /// - public static readonly Key D = new (KeyCode.D); + public static Key D => new (KeyCode.D); /// /// The object for the E key (un-shifted). Use Key.E.WithShift for uppercase 'E'. /// - public static readonly Key E = new (KeyCode.E); + public static Key E => new (KeyCode.E); /// /// The object for the F key (un-shifted). Use Key.F.WithShift for uppercase 'F'. /// - public static readonly Key F = new (KeyCode.F); + public static Key F => new (KeyCode.F); /// /// The object for the G key (un-shifted). Use Key.G.WithShift for uppercase 'G'. /// - public static readonly Key G = new (KeyCode.G); + public static Key G => new (KeyCode.G); /// /// The object for the H key (un-shifted). Use Key.H.WithShift for uppercase 'H'. /// - public static readonly Key H = new (KeyCode.H); + public static Key H => new (KeyCode.H); /// /// The object for the I key (un-shifted). Use Key.I.WithShift for uppercase 'I'. /// - public static readonly Key I = new (KeyCode.I); + public static Key I => new (KeyCode.I); /// /// The object for the J key (un-shifted). Use Key.J.WithShift for uppercase 'J'. /// - public static readonly Key J = new (KeyCode.J); + public static Key J => new (KeyCode.J); /// /// The object for the K key (un-shifted). Use Key.K.WithShift for uppercase 'K'. /// - public static readonly Key K = new (KeyCode.K); + public static Key K => new (KeyCode.K); /// /// The object for the L key (un-shifted). Use Key.L.WithShift for uppercase 'L'. /// - public static readonly Key L = new (KeyCode.L); + public static Key L => new (KeyCode.L); /// /// The object for the M key (un-shifted). Use Key.M.WithShift for uppercase 'M'. /// - public static readonly Key M = new (KeyCode.M); + public static Key M => new (KeyCode.M); /// /// The object for the N key (un-shifted). Use Key.N.WithShift for uppercase 'N'. /// - public static readonly Key N = new (KeyCode.N); + public static Key N => new (KeyCode.N); /// /// The object for the O key (un-shifted). Use Key.O.WithShift for uppercase 'O'. /// - public static readonly Key O = new (KeyCode.O); + public static Key O => new (KeyCode.O); /// /// The object for the P key (un-shifted). Use Key.P.WithShift for uppercase 'P'. /// - public static readonly Key P = new (KeyCode.P); + public static Key P => new (KeyCode.P); /// /// The object for the Q key (un-shifted). Use Key.Q.WithShift for uppercase 'Q'. /// - public static readonly Key Q = new (KeyCode.Q); + public static Key Q => new (KeyCode.Q); /// /// The object for the R key (un-shifted). Use Key.R.WithShift for uppercase 'R'. /// - public static readonly Key R = new (KeyCode.R); + public static Key R => new (KeyCode.R); /// /// The object for the S key (un-shifted). Use Key.S.WithShift for uppercase 'S'. /// - public static readonly Key S = new (KeyCode.S); + public static Key S => new (KeyCode.S); /// /// The object for the T key (un-shifted). Use Key.T.WithShift for uppercase 'T'. /// - public static readonly Key T = new (KeyCode.T); + public static Key T => new (KeyCode.T); /// /// The object for the U key (un-shifted). Use Key.U.WithShift for uppercase 'U'. /// - public static readonly Key U = new (KeyCode.U); + public static Key U => new (KeyCode.U); /// /// The object for the V key (un-shifted). Use Key.V.WithShift for uppercase 'V'. /// - public static readonly Key V = new (KeyCode.V); + public static Key V => new (KeyCode.V); /// /// The object for the W key (un-shifted). Use Key.W.WithShift for uppercase 'W'. /// - public static readonly Key W = new (KeyCode.W); + public static Key W => new (KeyCode.W); /// /// The object for the X key (un-shifted). Use Key.X.WithShift for uppercase 'X'. /// - public static readonly Key X = new (KeyCode.X); + public static Key X => new (KeyCode.X); /// /// The object for the Y key (un-shifted). Use Key.Y.WithShift for uppercase 'Y'. /// - public static readonly Key Y = new (KeyCode.Y); + public static Key Y => new (KeyCode.Y); /// /// The object for the Z key (un-shifted). Use Key.Z.WithShift for uppercase 'Z'. /// - public static readonly Key Z = new (KeyCode.Z); + public static Key Z => new (KeyCode.Z); /// /// The object for the Delete key. /// - public static readonly Key Delete = new (KeyCode.Delete); + public static Key Delete => new (KeyCode.Delete); /// /// The object for the Cursor up key. /// - public static readonly Key CursorUp = new (KeyCode.CursorUp); + public static Key CursorUp => new (KeyCode.CursorUp); /// /// The object for Cursor down key. /// - public static readonly Key CursorDown = new (KeyCode.CursorDown); + public static Key CursorDown => new (KeyCode.CursorDown); /// /// The object for Cursor left key. /// - public static readonly Key CursorLeft = new (KeyCode.CursorLeft); + public static Key CursorLeft => new (KeyCode.CursorLeft); /// /// The object for Cursor right key. /// - public static readonly Key CursorRight = new (KeyCode.CursorRight); + public static Key CursorRight => new (KeyCode.CursorRight); /// /// The object for Page Up key. /// - public static readonly Key PageUp = new (KeyCode.PageUp); + public static Key PageUp => new (KeyCode.PageUp); /// /// The object for Page Down key. /// - public static readonly Key PageDown = new (KeyCode.PageDown); + public static Key PageDown => new (KeyCode.PageDown); /// /// The object for Home key. /// - public static readonly Key Home = new (KeyCode.Home); + public static Key Home => new (KeyCode.Home); /// /// The object for End key. /// - public static readonly Key End = new (KeyCode.End); + public static Key End => new (KeyCode.End); /// /// The object for Insert Character key. /// - public static readonly Key InsertChar = new (KeyCode.InsertChar); + public static Key InsertChar => new (KeyCode.InsertChar); /// /// The object for Delete Character key. /// - public static readonly Key DeleteChar = new (KeyCode.DeleteChar); + public static Key DeleteChar => new (KeyCode.DeleteChar); /// /// The object for Print Screen key. /// - public static readonly Key PrintScreen = new (KeyCode.PrintScreen); + public static Key PrintScreen => new (KeyCode.PrintScreen); /// /// The object for F1 key. /// - public static readonly Key F1 = new (KeyCode.F1); + public static Key F1 => new (KeyCode.F1); /// /// The object for F2 key. /// - public static readonly Key F2 = new (KeyCode.F2); + public static Key F2 => new (KeyCode.F2); /// /// The object for F3 key. /// - public static readonly Key F3 = new (KeyCode.F3); + public static Key F3 => new (KeyCode.F3); /// /// The object for F4 key. /// - public static readonly Key F4 = new (KeyCode.F4); + public static Key F4 => new (KeyCode.F4); /// /// The object for F5 key. /// - public static readonly Key F5 = new (KeyCode.F5); + public static Key F5 => new (KeyCode.F5); /// /// The object for F6 key. /// - public static readonly Key F6 = new (KeyCode.F6); + public static Key F6 => new (KeyCode.F6); /// /// The object for F7 key. /// - public static readonly Key F7 = new (KeyCode.F7); + public static Key F7 => new (KeyCode.F7); /// /// The object for F8 key. /// - public static readonly Key F8 = new (KeyCode.F8); + public static Key F8 => new (KeyCode.F8); /// /// The object for F9 key. /// - public static readonly Key F9 = new (KeyCode.F9); + public static Key F9 => new (KeyCode.F9); /// /// The object for F10 key. /// - public static readonly Key F10 = new (KeyCode.F10); + public static Key F10 => new (KeyCode.F10); /// /// The object for F11 key. /// - public static readonly Key F11 = new (KeyCode.F11); + public static Key F11 => new (KeyCode.F11); /// /// The object for F12 key. /// - public static readonly Key F12 = new (KeyCode.F12); + public static Key F12 => new (KeyCode.F12); /// /// The object for F13 key. /// - public static readonly Key F13 = new (KeyCode.F13); + public static Key F13 => new (KeyCode.F13); /// /// The object for F14 key. /// - public static readonly Key F14 = new (KeyCode.F14); + public static Key F14 => new (KeyCode.F14); /// /// The object for F15 key. /// - public static readonly Key F15 = new (KeyCode.F15); + public static Key F15 => new (KeyCode.F15); /// /// The object for F16 key. /// - public static readonly Key F16 = new (KeyCode.F16); + public static Key F16 => new (KeyCode.F16); /// /// The object for F17 key. /// - public static readonly Key F17 = new (KeyCode.F17); + public static Key F17 => new (KeyCode.F17); /// /// The object for F18 key. /// - public static readonly Key F18 = new (KeyCode.F18); + public static Key F18 => new (KeyCode.F18); /// /// The object for F19 key. /// - public static readonly Key F19 = new (KeyCode.F19); + public static Key F19 => new (KeyCode.F19); /// /// The object for F20 key. /// - public static readonly Key F20 = new (KeyCode.F20); + public static Key F20 => new (KeyCode.F20); /// /// The object for F21 key. /// - public static readonly Key F21 = new (KeyCode.F21); + public static Key F21 => new (KeyCode.F21); /// /// The object for F22 key. /// - public static readonly Key F22 = new (KeyCode.F22); + public static Key F22 => new (KeyCode.F22); /// /// The object for F23 key. /// - public static readonly Key F23 = new (KeyCode.F23); + public static Key F23 => new (KeyCode.F23); /// /// The object for F24 key. /// - public static readonly Key F24 = new (KeyCode.F24); + public static Key F24 => new (KeyCode.F24); #endregion } \ No newline at end of file diff --git a/Terminal.Gui/Resources/config.json b/Terminal.Gui/Resources/config.json index af2c93e197..368ccd8bf5 100644 --- a/Terminal.Gui/Resources/config.json +++ b/Terminal.Gui/Resources/config.json @@ -16,15 +16,9 @@ // to throw exceptions. "ConfigurationManager.ThrowOnJsonErrors": false, - "Application.AlternateBackwardKey": { - "Key": "Ctrl+PageUp" - }, - "Application.AlternateForwardKey": { - "Key": "Ctrl+PageDown" - }, - "Application.QuitKey": { - "Key": "Ctrl+Q" - }, + "Application.AlternateBackwardKey": "Ctrl+PageUp", + "Application.AlternateForwardKey": "Ctrl+PageDown", + "Application.QuitKey": "Ctrl+Q", "Application.IsMouseDisabled": false, "Theme": "Default", "Themes": [ diff --git a/UICatalog/Resources/config.json b/UICatalog/Resources/config.json index 211d6c638d..4e651245ce 100644 --- a/UICatalog/Resources/config.json +++ b/UICatalog/Resources/config.json @@ -1,8 +1,6 @@ { "$schema": "https://gui-cs.github.io/Terminal.Gui/schemas/tui-config-schema.json", - "Application.QuitKey": { - "Key": "Esc" - }, + "Application.QuitKey": "Esc", "FileDialog.MaxSearchResults": 10000, "FileDialogStyle.DefaultUseColors": false, "FileDialogStyle.DefaultUseUnicodeCharacters": false, diff --git a/UICatalog/UICatalog.cs b/UICatalog/UICatalog.cs index 91c2a60fd3..6f715ee93d 100644 --- a/UICatalog/UICatalog.cs +++ b/UICatalog/UICatalog.cs @@ -516,7 +516,7 @@ MenuItem [] CreateDisabledEnabledMenuBorder () miIsMenuBorderDisabled = new MenuItem { Title = "Disable Menu _Border" }; - miIsMenuBorderDisabled.Shortcut = (KeyCode)((Key)miIsMenuBorderDisabled!.Title!.Substring (14, 1) [0]).WithAlt.WithCtrl; + miIsMenuBorderDisabled.Shortcut = (KeyCode)(new Key (miIsMenuBorderDisabled!.Title!.Substring (14, 1) [0])).WithAlt.WithCtrl; miIsMenuBorderDisabled.CheckType |= MenuItemCheckStyle.Checked; miIsMenuBorderDisabled.Action += () => { miIsMenuBorderDisabled.Checked = (bool)!miIsMenuBorderDisabled.Checked!; @@ -553,7 +553,7 @@ MenuItem [] CreateDisabledEnabledMouseItems () miIsMouseDisabled = new MenuItem { Title = "_Disable Mouse" }; - miIsMouseDisabled.Shortcut = (KeyCode)((Key)miIsMouseDisabled!.Title!.Substring (1, 1) [0]).WithAlt.WithCtrl; + miIsMouseDisabled.Shortcut = (KeyCode)(new Key (miIsMouseDisabled!.Title!.Substring (1, 1) [0])).WithAlt.WithCtrl; miIsMouseDisabled.CheckType |= MenuItemCheckStyle.Checked; miIsMouseDisabled.Action += () => { miIsMouseDisabled.Checked = Application.IsMouseDisabled = (bool)!miIsMouseDisabled.Checked!; @@ -592,7 +592,7 @@ MenuItem [] CreateDiagnosticFlagsMenuItems () foreach (Enum diag in Enum.GetValues (_diagnosticFlags.GetType ())) { var item = new MenuItem { Title = GetDiagnosticsTitle (diag), - Shortcut = (KeyCode)((Key)index.ToString () [0]).WithAlt + Shortcut = (KeyCode)(new Key(index.ToString () [0])).WithAlt }; index++; item.CheckType |= MenuItemCheckStyle.Checked; @@ -685,7 +685,7 @@ void SetDiagnosticsFlag (Enum diag, bool add) foreach (var theme in Themes!) { var item = new MenuItem { Title = $"_{theme.Key}", - Shortcut = (KeyCode)((Key)((int)KeyCode.D1 + schemeCount++)).WithCtrl + Shortcut = (KeyCode)(new Key ((KeyCode)((uint)KeyCode.D1 + (schemeCount++))).WithCtrl) }; item.CheckType |= MenuItemCheckStyle.Checked; item.Checked = theme.Key == _cachedTheme; // CM.Themes.Theme; diff --git a/UnitTests/Configuration/ConfigurationMangerTests.cs b/UnitTests/Configuration/ConfigurationMangerTests.cs index 70b2adadb3..5c9ed616e6 100644 --- a/UnitTests/Configuration/ConfigurationMangerTests.cs +++ b/UnitTests/Configuration/ConfigurationMangerTests.cs @@ -7,359 +7,357 @@ using Xunit; using static Terminal.Gui.ConfigurationManager; -namespace Terminal.Gui.ConfigurationTests { - public class ConfigurationManagerTests { +namespace Terminal.Gui.ConfigurationTests; - public static readonly JsonSerializerOptions _jsonOptions = new () { - Converters = { - new AttributeJsonConverter (), - new ColorJsonConverter (), - } - }; - - [Fact ()] - public void DeepMemberwiseCopyTest () - { - // Value types - var stringDest = "Destination"; - var stringSrc = "Source"; - var stringCopy = DeepMemberwiseCopy (stringSrc, stringDest); - Assert.Equal (stringSrc, stringCopy); - - stringDest = "Destination"; - stringSrc = "Destination"; - stringCopy = DeepMemberwiseCopy (stringSrc, stringDest); - Assert.Equal (stringSrc, stringCopy); - - stringDest = "Destination"; - stringSrc = null; - stringCopy = DeepMemberwiseCopy (stringSrc, stringDest); - Assert.Equal (stringSrc, stringCopy); - - stringDest = "Destination"; - stringSrc = string.Empty; - stringCopy = DeepMemberwiseCopy (stringSrc, stringDest); - Assert.Equal (stringSrc, stringCopy); - - var boolDest = true; - var boolSrc = false; - var boolCopy = DeepMemberwiseCopy (boolSrc, boolDest); - Assert.Equal (boolSrc, boolCopy); - - boolDest = false; - boolSrc = true; - boolCopy = DeepMemberwiseCopy (boolSrc, boolDest); - Assert.Equal (boolSrc, boolCopy); - - boolDest = true; - boolSrc = true; - boolCopy = DeepMemberwiseCopy (boolSrc, boolDest); - Assert.Equal (boolSrc, boolCopy); - - boolDest = false; - boolSrc = false; - boolCopy = DeepMemberwiseCopy (boolSrc, boolDest); - Assert.Equal (boolSrc, boolCopy); - - // Structs - var attrDest = new Attribute (Color.Black); - var attrSrc = new Attribute (Color.White); - var attrCopy = DeepMemberwiseCopy (attrSrc, attrDest); - Assert.Equal (attrSrc, attrCopy); - - // Classes - var colorschemeDest = new ColorScheme () { Disabled = new Attribute (Color.Black) }; - var colorschemeSrc = new ColorScheme () { Disabled = new Attribute (Color.White) }; - var colorschemeCopy = DeepMemberwiseCopy (colorschemeSrc, colorschemeDest); - Assert.Equal (colorschemeSrc, colorschemeCopy); - - // Dictionaries - var dictDest = new Dictionary () { { "Disabled", new Attribute (Color.Black) } }; - var dictSrc = new Dictionary () { { "Disabled", new Attribute (Color.White) } }; - var dictCopy = (Dictionary)DeepMemberwiseCopy (dictSrc, dictDest); - Assert.Equal (dictSrc, dictCopy); - - dictDest = new Dictionary () { { "Disabled", new Attribute (Color.Black) } }; - dictSrc = new Dictionary () { { "Disabled", new Attribute (Color.White) }, { "Normal", new Attribute (Color.Blue) } }; - dictCopy = (Dictionary)DeepMemberwiseCopy (dictSrc, dictDest); - Assert.Equal (dictSrc, dictCopy); - - // src adds an item - dictDest = new Dictionary () { { "Disabled", new Attribute (Color.Black) } }; - dictSrc = new Dictionary () { { "Disabled", new Attribute (Color.White) }, { "Normal", new Attribute (Color.Blue) } }; - dictCopy = (Dictionary)DeepMemberwiseCopy (dictSrc, dictDest); - Assert.Equal (2, dictCopy.Count); - Assert.Equal (dictSrc ["Disabled"], dictCopy ["Disabled"]); - Assert.Equal (dictSrc ["Normal"], dictCopy ["Normal"]); - - // src updates only one item - dictDest = new Dictionary () { { "Disabled", new Attribute (Color.Black) }, { "Normal", new Attribute (Color.White) } }; - dictSrc = new Dictionary () { { "Disabled", new Attribute (Color.White) } }; - dictCopy = (Dictionary)DeepMemberwiseCopy (dictSrc, dictDest); - Assert.Equal (2, dictCopy.Count); - Assert.Equal (dictSrc ["Disabled"], dictCopy ["Disabled"]); - Assert.Equal (dictDest ["Normal"], dictCopy ["Normal"]); - } - - //[Fact ()] - //public void LoadFromJsonTest () - //{ - // Assert.True (false, "This test needs an implementation"); - //} - - //[Fact ()] - //public void ToJsonTest () - //{ - // Assert.True (false, "This test needs an implementation"); - //} - - //[Fact ()] - //public void UpdateConfigurationTest () - //{ - // Assert.True (false, "This test needs an implementation"); - //} - - //[Fact ()] - //public void UpdateConfigurationFromFileTest () - //{ - // Assert.True (false, "This test needs an implementation"); - //} - - //[Fact ()] - //public void SaveHardCodedDefaultsTest () - //{ - // Assert.True (false, "This test needs an implementation"); - //} - - //[Fact ()] - //public void LoadGlobalFromLibraryResourceTest () - //{ - // Assert.True (false, "This test needs an implementation"); - //} - - //[Fact ()] - //public void LoadGlobalFromAppDirectoryTest () - //{ - // Assert.True (false, "This test needs an implementation"); - //} - - //[Fact ()] - //public void LoadGlobalFromHomeDirectoryTest () - //{ - // Assert.True (false, "This test needs an implementation"); - //} - - //[Fact ()] - //public void LoadAppFromAppResourcesTest () - //{ - // Assert.True (false, "This test needs an implementation"); - //} - - //[Fact ()] - //public void LoadAppFromAppDirectoryTest () - //{ - // Assert.True (false, "This test needs an implementation"); - //} - - //[Fact ()] - //public void LoadAppFromHomeDirectoryTest () - //{ - // Assert.True (false, "This test needs an implementation"); - //} - - //[Fact ()] - //public void LoadTest () - //{ - // Assert.True (false, "This test needs an implementation"); - //} - - /// - /// Save the `config.json` file; this can be used to update the file in `Terminal.Gui.Resources.config.json'. - /// - /// - /// IMPORTANT: For the file generated to be valid, this must be the ONLY test run. Config Properties - /// are all static and thus can be overwritten by other tests. - [Fact] - public void SaveDefaults () - { - ConfigurationManager.Initialize (); - ConfigurationManager.Reset (); - - // Get the hard coded settings - ConfigurationManager.GetHardCodedDefaults (); - - // Serialize to a JSON string - string json = ConfigurationManager.ToJson (); - - // Write the JSON string to the file - File.WriteAllText ("config.json", json); - } - - [Fact] - public void UseWithoutResetAsserts () - { - ConfigurationManager.Initialize (); - Assert.Throws (() => _ = ConfigurationManager.Settings); - } - - [Fact] - public void Reset_Resets () - { - ConfigurationManager.Locations = ConfigLocations.DefaultOnly; - ConfigurationManager.Reset (); - Assert.NotEmpty (ConfigurationManager.Themes); - Assert.Equal ("Default", ConfigurationManager.Themes.Theme); +public class ConfigurationManagerTests { + public static readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions { + Converters = { + new AttributeJsonConverter (), + new ColorJsonConverter () } + }; + + [Fact ()] + public void DeepMemberwiseCopyTest () + { + // Value types + string stringDest = "Destination"; + string stringSrc = "Source"; + object stringCopy = DeepMemberwiseCopy (stringSrc, stringDest); + Assert.Equal (stringSrc, stringCopy); + + stringDest = "Destination"; + stringSrc = "Destination"; + stringCopy = DeepMemberwiseCopy (stringSrc, stringDest); + Assert.Equal (stringSrc, stringCopy); + + stringDest = "Destination"; + stringSrc = null; + stringCopy = DeepMemberwiseCopy (stringSrc, stringDest); + Assert.Equal (stringSrc, stringCopy); + + stringDest = "Destination"; + stringSrc = string.Empty; + stringCopy = DeepMemberwiseCopy (stringSrc, stringDest); + Assert.Equal (stringSrc, stringCopy); + + bool boolDest = true; + bool boolSrc = false; + object boolCopy = DeepMemberwiseCopy (boolSrc, boolDest); + Assert.Equal (boolSrc, boolCopy); + + boolDest = false; + boolSrc = true; + boolCopy = DeepMemberwiseCopy (boolSrc, boolDest); + Assert.Equal (boolSrc, boolCopy); + + boolDest = true; + boolSrc = true; + boolCopy = DeepMemberwiseCopy (boolSrc, boolDest); + Assert.Equal (boolSrc, boolCopy); + + boolDest = false; + boolSrc = false; + boolCopy = DeepMemberwiseCopy (boolSrc, boolDest); + Assert.Equal (boolSrc, boolCopy); + + // Structs + var attrDest = new Attribute (Color.Black); + var attrSrc = new Attribute (Color.White); + object attrCopy = DeepMemberwiseCopy (attrSrc, attrDest); + Assert.Equal (attrSrc, attrCopy); + + // Classes + var colorschemeDest = new ColorScheme () { Disabled = new Attribute (Color.Black) }; + var colorschemeSrc = new ColorScheme () { Disabled = new Attribute (Color.White) }; + object colorschemeCopy = DeepMemberwiseCopy (colorschemeSrc, colorschemeDest); + Assert.Equal (colorschemeSrc, colorschemeCopy); + + // Dictionaries + var dictDest = new Dictionary () { { "Disabled", new Attribute (Color.Black) } }; + var dictSrc = new Dictionary () { { "Disabled", new Attribute (Color.White) } }; + var dictCopy = (Dictionary)DeepMemberwiseCopy (dictSrc, dictDest); + Assert.Equal (dictSrc, dictCopy); + + dictDest = new Dictionary () { { "Disabled", new Attribute (Color.Black) } }; + dictSrc = new Dictionary () { { "Disabled", new Attribute (Color.White) }, { "Normal", new Attribute (Color.Blue) } }; + dictCopy = (Dictionary)DeepMemberwiseCopy (dictSrc, dictDest); + Assert.Equal (dictSrc, dictCopy); + + // src adds an item + dictDest = new Dictionary () { { "Disabled", new Attribute (Color.Black) } }; + dictSrc = new Dictionary () { { "Disabled", new Attribute (Color.White) }, { "Normal", new Attribute (Color.Blue) } }; + dictCopy = (Dictionary)DeepMemberwiseCopy (dictSrc, dictDest); + Assert.Equal (2, dictCopy.Count); + Assert.Equal (dictSrc ["Disabled"], dictCopy ["Disabled"]); + Assert.Equal (dictSrc ["Normal"], dictCopy ["Normal"]); + + // src updates only one item + dictDest = new Dictionary () { { "Disabled", new Attribute (Color.Black) }, { "Normal", new Attribute (Color.White) } }; + dictSrc = new Dictionary () { { "Disabled", new Attribute (Color.White) } }; + dictCopy = (Dictionary)DeepMemberwiseCopy (dictSrc, dictDest); + Assert.Equal (2, dictCopy.Count); + Assert.Equal (dictSrc ["Disabled"], dictCopy ["Disabled"]); + Assert.Equal (dictDest ["Normal"], dictCopy ["Normal"]); + } - [Fact] - public void Reset_and_ResetLoadWithLibraryResourcesOnly_are_same () - { - ConfigurationManager.Locations = ConfigLocations.DefaultOnly; - // arrange - ConfigurationManager.Reset (); - ConfigurationManager.Settings ["Application.QuitKey"].PropertyValue = new Key (KeyCode.Q); - ConfigurationManager.Settings ["Application.AlternateForwardKey"].PropertyValue = new Key (KeyCode.F); - ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue = new Key (KeyCode.B); - ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue = true; - ConfigurationManager.Settings.Apply (); - - // assert apply worked - Assert.Equal (KeyCode.Q, Application.QuitKey.KeyCode); - Assert.Equal (KeyCode.F, Application.AlternateForwardKey.KeyCode); - Assert.Equal (KeyCode.B, Application.AlternateBackwardKey.KeyCode); - Assert.True (Application.IsMouseDisabled); + //[Fact ()] + //public void LoadFromJsonTest () + //{ + // Assert.True (false, "This test needs an implementation"); + //} + + //[Fact ()] + //public void ToJsonTest () + //{ + // Assert.True (false, "This test needs an implementation"); + //} + + //[Fact ()] + //public void UpdateConfigurationTest () + //{ + // Assert.True (false, "This test needs an implementation"); + //} + + //[Fact ()] + //public void UpdateConfigurationFromFileTest () + //{ + // Assert.True (false, "This test needs an implementation"); + //} + + //[Fact ()] + //public void SaveHardCodedDefaultsTest () + //{ + // Assert.True (false, "This test needs an implementation"); + //} + + //[Fact ()] + //public void LoadGlobalFromLibraryResourceTest () + //{ + // Assert.True (false, "This test needs an implementation"); + //} + + //[Fact ()] + //public void LoadGlobalFromAppDirectoryTest () + //{ + // Assert.True (false, "This test needs an implementation"); + //} + + //[Fact ()] + //public void LoadGlobalFromHomeDirectoryTest () + //{ + // Assert.True (false, "This test needs an implementation"); + //} + + //[Fact ()] + //public void LoadAppFromAppResourcesTest () + //{ + // Assert.True (false, "This test needs an implementation"); + //} + + //[Fact ()] + //public void LoadAppFromAppDirectoryTest () + //{ + // Assert.True (false, "This test needs an implementation"); + //} + + //[Fact ()] + //public void LoadAppFromHomeDirectoryTest () + //{ + // Assert.True (false, "This test needs an implementation"); + //} + + //[Fact ()] + //public void LoadTest () + //{ + // Assert.True (false, "This test needs an implementation"); + //} + + /// + /// Save the `config.json` file; this can be used to update the file in `Terminal.Gui.Resources.config.json'. + /// + /// + /// IMPORTANT: For the file generated to be valid, this must be the ONLY test run. Config Properties + /// are all static and thus can be overwritten by other tests. + [Fact] + public void SaveDefaults () + { + Initialize (); + Reset (); + + // Get the hard coded settings + GetHardCodedDefaults (); + + // Serialize to a JSON string + string json = ToJson (); + + // Write the JSON string to the file + File.WriteAllText ("config.json", json); + } - //act - ConfigurationManager.Reset (); + [Fact] + public void UseWithoutResetAsserts () + { + Initialize (); + Assert.Throws (() => _ = Settings); + } - // assert - Assert.NotEmpty (ConfigurationManager.Themes); - Assert.Equal ("Default", ConfigurationManager.Themes.Theme); - Assert.Equal (KeyCode.Q | KeyCode.CtrlMask, Application.QuitKey.KeyCode); - Assert.Equal (KeyCode.PageDown | KeyCode.CtrlMask, Application.AlternateForwardKey.KeyCode); - Assert.Equal (KeyCode.PageUp | KeyCode.CtrlMask, Application.AlternateBackwardKey.KeyCode); - Assert.False (Application.IsMouseDisabled); - - // arrange - ConfigurationManager.Settings ["Application.QuitKey"].PropertyValue = new Key (KeyCode.Q); - ConfigurationManager.Settings ["Application.AlternateForwardKey"].PropertyValue = new Key (KeyCode.F); - ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue = new Key (KeyCode.B); - ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue = true; - ConfigurationManager.Settings.Apply (); - - ConfigurationManager.Locations = ConfigLocations.DefaultOnly; - - // act - ConfigurationManager.Reset (); - ConfigurationManager.Load (); + [Fact] + public void Reset_Resets () + { + Locations = ConfigLocations.DefaultOnly; + Reset (); + Assert.NotEmpty (Themes); + Assert.Equal ("Default", Themes.Theme); + } - // assert - Assert.NotEmpty (ConfigurationManager.Themes); - Assert.Equal ("Default", ConfigurationManager.Themes.Theme); - Assert.Equal (KeyCode.Q | KeyCode.CtrlMask, Application.QuitKey.KeyCode); - Assert.Equal (KeyCode.PageDown | KeyCode.CtrlMask, Application.AlternateForwardKey.KeyCode); - Assert.Equal (KeyCode.PageUp | KeyCode.CtrlMask, Application.AlternateBackwardKey.KeyCode); - Assert.False (Application.IsMouseDisabled); + [Fact] + public void Reset_and_ResetLoadWithLibraryResourcesOnly_are_same () + { + Locations = ConfigLocations.DefaultOnly; + // arrange + Reset (); + Settings ["Application.QuitKey"].PropertyValue = new Key (KeyCode.Q); + Settings ["Application.AlternateForwardKey"].PropertyValue = new Key (KeyCode.F); + Settings ["Application.AlternateBackwardKey"].PropertyValue = new Key (KeyCode.B); + Settings ["Application.IsMouseDisabled"].PropertyValue = true; + Settings.Apply (); + + // assert apply worked + Assert.Equal (KeyCode.Q, Application.QuitKey.KeyCode); + Assert.Equal (KeyCode.F, Application.AlternateForwardKey.KeyCode); + Assert.Equal (KeyCode.B, Application.AlternateBackwardKey.KeyCode); + Assert.True (Application.IsMouseDisabled); + + //act + Reset (); + + // assert + Assert.NotEmpty (Themes); + Assert.Equal ("Default", Themes.Theme); + Assert.Equal (KeyCode.Q | KeyCode.CtrlMask, Application.QuitKey.KeyCode); + Assert.Equal (KeyCode.PageDown | KeyCode.CtrlMask, Application.AlternateForwardKey.KeyCode); + Assert.Equal (KeyCode.PageUp | KeyCode.CtrlMask, Application.AlternateBackwardKey.KeyCode); + Assert.False (Application.IsMouseDisabled); + + // arrange + Settings ["Application.QuitKey"].PropertyValue = new Key (KeyCode.Q); + Settings ["Application.AlternateForwardKey"].PropertyValue = new Key (KeyCode.F); + Settings ["Application.AlternateBackwardKey"].PropertyValue = new Key (KeyCode.B); + Settings ["Application.IsMouseDisabled"].PropertyValue = true; + Settings.Apply (); + + Locations = ConfigLocations.DefaultOnly; + + // act + Reset (); + Load (); + + // assert + Assert.NotEmpty (Themes); + Assert.Equal ("Default", Themes.Theme); + Assert.Equal (KeyCode.Q | KeyCode.CtrlMask, Application.QuitKey.KeyCode); + Assert.Equal (KeyCode.PageDown | KeyCode.CtrlMask, Application.AlternateForwardKey.KeyCode); + Assert.Equal (KeyCode.PageUp | KeyCode.CtrlMask, Application.AlternateBackwardKey.KeyCode); + Assert.False (Application.IsMouseDisabled); - } + } - [Fact] - public void TestConfigProperties () - { - ConfigurationManager.Locations = ConfigLocations.All; - ConfigurationManager.Reset (); - - Assert.NotEmpty (ConfigurationManager.Settings); - // test that all ConfigProperites have our attribute - Assert.All (ConfigurationManager.Settings, item => Assert.NotEmpty (item.Value.PropertyInfo.CustomAttributes.Where (a => a.AttributeType == typeof (SerializableConfigurationProperty)))); - Assert.Empty (ConfigurationManager.Settings.Where (cp => cp.Value.PropertyInfo.GetCustomAttribute (typeof (SerializableConfigurationProperty)) == null)); - - // Application is a static class - PropertyInfo pi = typeof (Application).GetProperty ("QuitKey"); - Assert.Equal (pi, ConfigurationManager.Settings ["Application.QuitKey"].PropertyInfo); - - // FrameView is not a static class and DefaultBorderStyle is Scope.Scheme - pi = typeof (FrameView).GetProperty ("DefaultBorderStyle"); - Assert.False (ConfigurationManager.Settings.ContainsKey ("FrameView.DefaultBorderStyle")); - Assert.True (ConfigurationManager.Themes ["Default"].ContainsKey ("FrameView.DefaultBorderStyle")); - } + [Fact] + public void TestConfigProperties () + { + Locations = ConfigLocations.All; + Reset (); + + Assert.NotEmpty (Settings); + // test that all ConfigProperites have our attribute + Assert.All (Settings, item => Assert.NotEmpty (item.Value.PropertyInfo.CustomAttributes.Where (a => a.AttributeType == typeof (SerializableConfigurationProperty)))); + Assert.Empty (Settings.Where (cp => cp.Value.PropertyInfo.GetCustomAttribute (typeof (SerializableConfigurationProperty)) == null)); + + // Application is a static class + var pi = typeof (Application).GetProperty ("QuitKey"); + Assert.Equal (pi, Settings ["Application.QuitKey"].PropertyInfo); + + // FrameView is not a static class and DefaultBorderStyle is Scope.Scheme + pi = typeof (FrameView).GetProperty ("DefaultBorderStyle"); + Assert.False (Settings.ContainsKey ("FrameView.DefaultBorderStyle")); + Assert.True (Themes ["Default"].ContainsKey ("FrameView.DefaultBorderStyle")); + } - [Fact] - public void TestConfigPropertyOmitClassName () - { - // Color.ColorShemes is serialzied as "ColorSchemes", not "Colors.ColorSchemes" - PropertyInfo pi = typeof (Colors).GetProperty ("ColorSchemes"); - var scp = ((SerializableConfigurationProperty)pi.GetCustomAttribute (typeof (SerializableConfigurationProperty))); - Assert.True (scp.Scope == typeof (ThemeScope)); - Assert.True (scp.OmitClassName); - - ConfigurationManager.Reset (); - Assert.Equal (pi, ConfigurationManager.Themes ["Default"] ["ColorSchemes"].PropertyInfo); - } + [Fact] + public void TestConfigPropertyOmitClassName () + { + // Color.ColorShemes is serialzied as "ColorSchemes", not "Colors.ColorSchemes" + var pi = typeof (Colors).GetProperty ("ColorSchemes"); + var scp = (SerializableConfigurationProperty)pi.GetCustomAttribute (typeof (SerializableConfigurationProperty)); + Assert.True (scp.Scope == typeof (ThemeScope)); + Assert.True (scp.OmitClassName); + + Reset (); + Assert.Equal (pi, Themes ["Default"] ["ColorSchemes"].PropertyInfo); + } - [Fact, AutoInitShutdown] - public void TestConfigurationManagerToJson () - { - ConfigurationManager.Reset (); - ConfigurationManager.GetHardCodedDefaults (); - var stream = ConfigurationManager.ToStream (); + [Fact] [AutoInitShutdown] + public void TestConfigurationManagerToJson () + { + Reset (); + GetHardCodedDefaults (); + var stream = ToStream (); - ConfigurationManager.Settings.Update (stream, "TestConfigurationManagerToJson"); - } + Settings.Update (stream, "TestConfigurationManagerToJson"); + } - [Fact, AutoInitShutdown (configLocation: ConfigLocations.None)] - public void TestConfigurationManagerInitDriver_NoLocations () - { + [Fact] [AutoInitShutdown (configLocation: ConfigLocations.None)] + public void TestConfigurationManagerInitDriver_NoLocations () + { - } + } - [Fact, AutoInitShutdown (configLocation: ConfigLocations.DefaultOnly)] - public void TestConfigurationManagerInitDriver () - { - Assert.Equal ("Default", ConfigurationManager.Themes.Theme); - Assert.True (ConfigurationManager.Themes.ContainsKey ("Default")); + [Fact] [AutoInitShutdown (configLocation: ConfigLocations.DefaultOnly)] + public void TestConfigurationManagerInitDriver () + { + Assert.Equal ("Default", Themes.Theme); + Assert.True (Themes.ContainsKey ("Default")); - Assert.Equal (KeyCode.Q | KeyCode.CtrlMask, Application.QuitKey.KeyCode); + Assert.Equal (KeyCode.Q | KeyCode.CtrlMask, Application.QuitKey.KeyCode); - Assert.Equal (new Color (Color.White), Colors.ColorSchemes ["Base"].Normal.Foreground); - Assert.Equal (new Color (Color.Blue), Colors.ColorSchemes ["Base"].Normal.Background); + Assert.Equal (new Color (Color.White), Colors.ColorSchemes ["Base"].Normal.Foreground); + Assert.Equal (new Color (Color.Blue), Colors.ColorSchemes ["Base"].Normal.Background); - // Change Base - var json = ConfigurationManager.ToStream (); + // Change Base + var json = ToStream (); - ConfigurationManager.Settings.Update (json, "TestConfigurationManagerInitDriver"); + Settings.Update (json, "TestConfigurationManagerInitDriver"); - var colorSchemes = ((Dictionary)ConfigurationManager.Themes [ConfigurationManager.Themes.Theme] ["ColorSchemes"].PropertyValue); - Assert.Equal (Colors.Base, colorSchemes ["Base"]); - Assert.Equal (Colors.TopLevel, colorSchemes ["TopLevel"]); - Assert.Equal (Colors.Error, colorSchemes ["Error"]); - Assert.Equal (Colors.Dialog, colorSchemes ["Dialog"]); - Assert.Equal (Colors.Menu, colorSchemes ["Menu"]); + var colorSchemes = (Dictionary)Themes [Themes.Theme] ["ColorSchemes"].PropertyValue; + Assert.Equal (Colors.Base, colorSchemes ["Base"]); + Assert.Equal (Colors.TopLevel, colorSchemes ["TopLevel"]); + Assert.Equal (Colors.Error, colorSchemes ["Error"]); + Assert.Equal (Colors.Dialog, colorSchemes ["Dialog"]); + Assert.Equal (Colors.Menu, colorSchemes ["Menu"]); - Colors.Base = colorSchemes ["Base"]; - Colors.TopLevel = colorSchemes ["TopLevel"]; - Colors.Error = colorSchemes ["Error"]; - Colors.Dialog = colorSchemes ["Dialog"]; - Colors.Menu = colorSchemes ["Menu"]; + Colors.Base = colorSchemes ["Base"]; + Colors.TopLevel = colorSchemes ["TopLevel"]; + Colors.Error = colorSchemes ["Error"]; + Colors.Dialog = colorSchemes ["Dialog"]; + Colors.Menu = colorSchemes ["Menu"]; - Assert.Equal (colorSchemes ["Base"], Colors.Base); - Assert.Equal (colorSchemes ["TopLevel"], Colors.TopLevel); - Assert.Equal (colorSchemes ["Error"], Colors.Error); - Assert.Equal (colorSchemes ["Dialog"], Colors.Dialog); - Assert.Equal (colorSchemes ["Menu"], Colors.Menu); - } + Assert.Equal (colorSchemes ["Base"], Colors.Base); + Assert.Equal (colorSchemes ["TopLevel"], Colors.TopLevel); + Assert.Equal (colorSchemes ["Error"], Colors.Error); + Assert.Equal (colorSchemes ["Dialog"], Colors.Dialog); + Assert.Equal (colorSchemes ["Menu"], Colors.Menu); + } - [Fact] - public void TestConfigurationManagerUpdateFromJson () - { - // Arrange - string json = @" + [Fact] + public void TestConfigurationManagerUpdateFromJson () + { + // Arrange + string json = @" { ""$schema"": ""https://gui-cs.github.io/Terminal.Gui/schemas/tui-config-schema.json"", - ""Application.QuitKey"": { - ""Key"": ""Alt-Z"" - }, + ""Application.QuitKey"": ""Alt-Z"", ""Theme"": ""Default"", ""Themes"": [ { @@ -493,39 +491,39 @@ public void TestConfigurationManagerUpdateFromJson () } "; - ConfigurationManager.Reset (); - ConfigurationManager.ThrowOnJsonErrors = true; + Reset (); + ThrowOnJsonErrors = true; - ConfigurationManager.Settings.Update (json, "TestConfigurationManagerUpdateFromJson"); - - Assert.Equal (KeyCode.Q | KeyCode.CtrlMask, Application.QuitKey.KeyCode); - Assert.Equal (KeyCode.Z | KeyCode.AltMask, ((Key)ConfigurationManager.Settings ["Application.QuitKey"].PropertyValue).KeyCode); + Settings.Update (json, "TestConfigurationManagerUpdateFromJson"); - Assert.Equal ("Default", ConfigurationManager.Themes.Theme); + Assert.Equal (KeyCode.Q | KeyCode.CtrlMask, Application.QuitKey.KeyCode); + Assert.Equal (KeyCode.Z | KeyCode.AltMask, ((Key)Settings ["Application.QuitKey"].PropertyValue).KeyCode); - Assert.Equal (new Color (Color.White), Colors.ColorSchemes ["Base"].Normal.Foreground); - Assert.Equal (new Color (Color.Blue), Colors.ColorSchemes ["Base"].Normal.Background); + Assert.Equal ("Default", Themes.Theme); - var colorSchemes = (Dictionary)Themes.First ().Value ["ColorSchemes"].PropertyValue; - Assert.Equal (new Color (Color.White), colorSchemes ["Base"].Normal.Foreground); - Assert.Equal (new Color (Color.Blue), colorSchemes ["Base"].Normal.Background); + Assert.Equal (new Color (Color.White), Colors.ColorSchemes ["Base"].Normal.Foreground); + Assert.Equal (new Color (Color.Blue), Colors.ColorSchemes ["Base"].Normal.Background); - // Now re-apply - ConfigurationManager.Apply (); + var colorSchemes = (Dictionary)Themes.First ().Value ["ColorSchemes"].PropertyValue; + Assert.Equal (new Color (Color.White), colorSchemes ["Base"].Normal.Foreground); + Assert.Equal (new Color (Color.Blue), colorSchemes ["Base"].Normal.Background); - Assert.Equal (KeyCode.Z | KeyCode.AltMask, Application.QuitKey.KeyCode); - Assert.Equal ("Default", ConfigurationManager.Themes.Theme); + // Now re-apply + Apply (); - Assert.Equal (new Color (Color.White), Colors.ColorSchemes ["Base"].Normal.Foreground); - Assert.Equal (new Color (Color.Blue), Colors.ColorSchemes ["Base"].Normal.Background); - } + Assert.Equal (KeyCode.Z | KeyCode.AltMask, Application.QuitKey.KeyCode); + Assert.Equal ("Default", Themes.Theme); - [Fact, AutoInitShutdown] - public void TestConfigurationManagerInvalidJsonThrows () - { - ConfigurationManager.ThrowOnJsonErrors = true; - // "yellow" is not a color - string json = @" + Assert.Equal (new Color (Color.White), Colors.ColorSchemes ["Base"].Normal.Foreground); + Assert.Equal (new Color (Color.Blue), Colors.ColorSchemes ["Base"].Normal.Background); + } + + [Fact] [AutoInitShutdown] + public void TestConfigurationManagerInvalidJsonThrows () + { + ThrowOnJsonErrors = true; + // "yellow" is not a color + string json = @" { ""Themes"" : [ { @@ -545,11 +543,11 @@ public void TestConfigurationManagerInvalidJsonThrows () ] }"; - JsonException jsonException = Assert.Throws (() => ConfigurationManager.Settings.Update (json, "test")); - Assert.Equal ("Unexpected color name: brown.", jsonException.Message); + var jsonException = Assert.Throws (() => Settings.Update (json, "test")); + Assert.Equal ("Unexpected color name: brown.", jsonException.Message); - // AbNormal is not a ColorScheme attribute - json = @" + // AbNormal is not a ColorScheme attribute + json = @" { ""Themes"" : [ { @@ -569,11 +567,11 @@ public void TestConfigurationManagerInvalidJsonThrows () ] }"; - jsonException = Assert.Throws (() => ConfigurationManager.Settings.Update (json, "test")); - Assert.Equal ("Unrecognized ColorScheme Attribute name: AbNormal.", jsonException.Message); + jsonException = Assert.Throws (() => Settings.Update (json, "test")); + Assert.Equal ("Unrecognized ColorScheme Attribute name: AbNormal.", jsonException.Message); - // Modify hotNormal background only - json = @" + // Modify hotNormal background only + json = @" { ""Themes"" : [ { @@ -592,31 +590,31 @@ public void TestConfigurationManagerInvalidJsonThrows () ] }"; - jsonException = Assert.Throws (() => ConfigurationManager.Settings.Update (json, "test")); - Assert.Equal ("Both Foreground and Background colors must be provided.", jsonException.Message); + jsonException = Assert.Throws (() => Settings.Update (json, "test")); + Assert.Equal ("Both Foreground and Background colors must be provided.", jsonException.Message); - // Unknown proeprty - json = @" + // Unknown proeprty + json = @" { ""Unknown"" : ""Not known"" }"; - jsonException = Assert.Throws (() => ConfigurationManager.Settings.Update (json, "test")); - Assert.StartsWith ("Unknown property", jsonException.Message); + jsonException = Assert.Throws (() => Settings.Update (json, "test")); + Assert.StartsWith ("Unknown property", jsonException.Message); - Assert.Equal (0, ConfigurationManager.jsonErrors.Length); + Assert.Equal (0, jsonErrors.Length); - ConfigurationManager.ThrowOnJsonErrors = false; - } + ThrowOnJsonErrors = false; + } - [Fact] - public void TestConfigurationManagerInvalidJsonLogs () - { - Application.Init (new FakeDriver ()); + [Fact] + public void TestConfigurationManagerInvalidJsonLogs () + { + Application.Init (new FakeDriver ()); - ConfigurationManager.ThrowOnJsonErrors = false; - // "brown" is not a color - string json = @" + ThrowOnJsonErrors = false; + // "brown" is not a color + string json = @" { ""Themes"" : [ { @@ -636,10 +634,10 @@ public void TestConfigurationManagerInvalidJsonLogs () } }"; - ConfigurationManager.Settings.Update (json, "test"); + Settings.Update (json, "test"); - // AbNormal is not a ColorScheme attribute - json = @" + // AbNormal is not a ColorScheme attribute + json = @" { ""Themes"" : [ { @@ -659,10 +657,10 @@ public void TestConfigurationManagerInvalidJsonLogs () } }"; - ConfigurationManager.Settings.Update (json, "test"); + Settings.Update (json, "test"); - // Modify hotNormal background only - json = @" + // Modify hotNormal background only + json = @" { ""Themes"" : [ { @@ -681,111 +679,109 @@ public void TestConfigurationManagerInvalidJsonLogs () } }"; - ConfigurationManager.Settings.Update (json, "test"); + Settings.Update (json, "test"); - ConfigurationManager.Settings.Update ("{}}", "test"); + Settings.Update ("{}}", "test"); - Assert.NotEqual (0, ConfigurationManager.jsonErrors.Length); + Assert.NotEqual (0, jsonErrors.Length); - Application.Shutdown (); + Application.Shutdown (); - ConfigurationManager.ThrowOnJsonErrors = false; - } + ThrowOnJsonErrors = false; + } - [Fact, AutoInitShutdown] - public void LoadConfigurationFromAllSources_ShouldLoadSettingsFromAllSources () - { - //var _configFilename = "config.json"; - //// Arrange - //// Create a mock of the configuration files in all sources - //// Home directory - //string homeDir = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.UserProfile), ".tui"); - //if (!Directory.Exists (homeDir)) { - // Directory.CreateDirectory (homeDir); - //} - //string globalConfigFile = Path.Combine (homeDir, _configFilename); - //string appSpecificConfigFile = Path.Combine (homeDir, "appname.config.json"); - //File.WriteAllText (globalConfigFile, "{\"Settings\": {\"TestSetting\":\"Global\"}}"); - //File.WriteAllText (appSpecificConfigFile, "{\"Settings\": {\"TestSetting\":\"AppSpecific\"}}"); - - //// App directory - //string appDir = Directory.GetCurrentDirectory (); - //string appDirGlobalConfigFile = Path.Combine (appDir, _configFilename); - //string appDirAppSpecificConfigFile = Path.Combine (appDir, "appname.config.json"); - //File.WriteAllText (appDirGlobalConfigFile, "{\"Settings\": {\"TestSetting\":\"GlobalAppDir\"}}"); - //File.WriteAllText (appDirAppSpecificConfigFile, "{\"Settings\": {\"TestSetting\":\"AppSpecificAppDir\"}}"); - - //// App resources - //// ... - - //// Act - //ConfigurationManager.Locations = ConfigurationManager.ConfigLocation.All; - //ConfigurationManager.Load (); - - //// Assert - //// Check that the settings from the highest precedence source are loaded - //Assert.Equal ("AppSpecific", ConfigurationManager.Config.Settings.TestSetting); - } + [Fact] [AutoInitShutdown] + public void LoadConfigurationFromAllSources_ShouldLoadSettingsFromAllSources () + { + //var _configFilename = "config.json"; + //// Arrange + //// Create a mock of the configuration files in all sources + //// Home directory + //string homeDir = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.UserProfile), ".tui"); + //if (!Directory.Exists (homeDir)) { + // Directory.CreateDirectory (homeDir); + //} + //string globalConfigFile = Path.Combine (homeDir, _configFilename); + //string appSpecificConfigFile = Path.Combine (homeDir, "appname.config.json"); + //File.WriteAllText (globalConfigFile, "{\"Settings\": {\"TestSetting\":\"Global\"}}"); + //File.WriteAllText (appSpecificConfigFile, "{\"Settings\": {\"TestSetting\":\"AppSpecific\"}}"); + + //// App directory + //string appDir = Directory.GetCurrentDirectory (); + //string appDirGlobalConfigFile = Path.Combine (appDir, _configFilename); + //string appDirAppSpecificConfigFile = Path.Combine (appDir, "appname.config.json"); + //File.WriteAllText (appDirGlobalConfigFile, "{\"Settings\": {\"TestSetting\":\"GlobalAppDir\"}}"); + //File.WriteAllText (appDirAppSpecificConfigFile, "{\"Settings\": {\"TestSetting\":\"AppSpecificAppDir\"}}"); + + //// App resources + //// ... + + //// Act + //ConfigurationManager.Locations = ConfigurationManager.ConfigLocation.All; + //ConfigurationManager.Load (); + + //// Assert + //// Check that the settings from the highest precedence source are loaded + //Assert.Equal ("AppSpecific", ConfigurationManager.Config.Settings.TestSetting); + } - [Fact] - public void Load_FiresUpdated () + [Fact] + public void Load_FiresUpdated () + { + Reset (); + + Settings ["Application.QuitKey"].PropertyValue = new Key (KeyCode.Q); + Settings ["Application.AlternateForwardKey"].PropertyValue = new Key (KeyCode.F); + Settings ["Application.AlternateBackwardKey"].PropertyValue = new Key (KeyCode.B); + Settings ["Application.IsMouseDisabled"].PropertyValue = true; + + Updated += ConfigurationManager_Updated; + bool fired = false; + void ConfigurationManager_Updated (object sender, ConfigurationManagerEventArgs obj) { - ConfigurationManager.Reset (); + fired = true; + // assert + Assert.Equal (KeyCode.Q | KeyCode.CtrlMask, ((Key)Settings ["Application.QuitKey"].PropertyValue).KeyCode); + Assert.Equal (KeyCode.PageDown | KeyCode.CtrlMask, ((Key)Settings ["Application.AlternateForwardKey"].PropertyValue).KeyCode); + Assert.Equal (KeyCode.PageUp | KeyCode.CtrlMask, ((Key)Settings ["Application.AlternateBackwardKey"].PropertyValue).KeyCode); + Assert.False ((bool)Settings ["Application.IsMouseDisabled"].PropertyValue); + } - ConfigurationManager.Settings ["Application.QuitKey"].PropertyValue = new Key (KeyCode.Q); - ConfigurationManager.Settings ["Application.AlternateForwardKey"].PropertyValue = new Key (KeyCode.F); - ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue = new Key (KeyCode.B); - ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue = true; + Load (true); - ConfigurationManager.Updated += ConfigurationManager_Updated; - bool fired = false; - void ConfigurationManager_Updated (object sender, ConfigurationManagerEventArgs obj) - { - fired = true; - // assert - Assert.Equal (KeyCode.Q | KeyCode.CtrlMask, ((Key)ConfigurationManager.Settings ["Application.QuitKey"].PropertyValue).KeyCode); - Assert.Equal (KeyCode.PageDown | KeyCode.CtrlMask, ((Key)ConfigurationManager.Settings ["Application.AlternateForwardKey"].PropertyValue).KeyCode); - Assert.Equal (KeyCode.PageUp | KeyCode.CtrlMask, ((Key)ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue).KeyCode); - Assert.False ((bool)ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue); - } + // assert + Assert.True (fired); - ConfigurationManager.Load (true); + Updated -= ConfigurationManager_Updated; + } + [Fact] + public void Apply_FiresApplied () + { + Reset (); + Applied += ConfigurationManager_Applied; + bool fired = false; + void ConfigurationManager_Applied (object sender, ConfigurationManagerEventArgs obj) + { + fired = true; // assert - Assert.True (fired); - - ConfigurationManager.Updated -= ConfigurationManager_Updated; + Assert.Equal (KeyCode.Q, Application.QuitKey.KeyCode); + Assert.Equal (KeyCode.F, Application.AlternateForwardKey.KeyCode); + Assert.Equal (KeyCode.B, Application.AlternateBackwardKey.KeyCode); + Assert.True (Application.IsMouseDisabled); } - [Fact] - public void Apply_FiresApplied () - { - ConfigurationManager.Reset (); - ConfigurationManager.Applied += ConfigurationManager_Applied; - bool fired = false; - void ConfigurationManager_Applied (object sender, ConfigurationManagerEventArgs obj) - { - fired = true; - // assert - Assert.Equal (KeyCode.Q, Application.QuitKey.KeyCode); - Assert.Equal (KeyCode.F, Application.AlternateForwardKey.KeyCode); - Assert.Equal (KeyCode.B, Application.AlternateBackwardKey.KeyCode); - Assert.True (Application.IsMouseDisabled); - } - - // act - ConfigurationManager.Settings ["Application.QuitKey"].PropertyValue = new Key (KeyCode.Q); - ConfigurationManager.Settings ["Application.AlternateForwardKey"].PropertyValue = new Key (KeyCode.F); - ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue = new Key (KeyCode.B); - ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue = true; - - ConfigurationManager.Apply (); + // act + Settings ["Application.QuitKey"].PropertyValue = new Key (KeyCode.Q); + Settings ["Application.AlternateForwardKey"].PropertyValue = new Key (KeyCode.F); + Settings ["Application.AlternateBackwardKey"].PropertyValue = new Key (KeyCode.B); + Settings ["Application.IsMouseDisabled"].PropertyValue = true; - // assert - Assert.True (fired); + Apply (); - ConfigurationManager.Applied -= ConfigurationManager_Applied; - } - } -} + // assert + Assert.True (fired); + Applied -= ConfigurationManager_Applied; + } +} \ No newline at end of file diff --git a/UnitTests/Configuration/JsonConverterTests.cs b/UnitTests/Configuration/JsonConverterTests.cs index 41a5650bb0..bc3f46924c 100644 --- a/UnitTests/Configuration/JsonConverterTests.cs +++ b/UnitTests/Configuration/JsonConverterTests.cs @@ -278,16 +278,16 @@ public void TestKeyRoundTripConversion (KeyCode key, string expectedStringTo) public class KeyJsonConverterTests { [Theory] - [InlineData (KeyCode.A, "{\"Key\":\"a\"}")] - [InlineData ((KeyCode)'â', "{\"Key\":\"â\"}")] - [InlineData (KeyCode.A | KeyCode.ShiftMask, "{\"Key\":\"A\"}")] - [InlineData (KeyCode.A | KeyCode.CtrlMask, "{\"Key\":\"Ctrl+A\"}")] - [InlineData (KeyCode.A | KeyCode.AltMask | KeyCode.CtrlMask, "{\"Key\":\"Ctrl+Alt+A\"}")] - [InlineData ((KeyCode)'a' | KeyCode.AltMask | KeyCode.CtrlMask, "{\"Key\":\"Ctrl+Alt+A\"}")] - [InlineData ((KeyCode)'a' | KeyCode.ShiftMask, "{\"Key\":\"A\"}")] - [InlineData (KeyCode.Delete | KeyCode.AltMask | KeyCode.CtrlMask, "{\"Key\":\"Ctrl+Alt+Delete\"}")] - [InlineData (KeyCode.D4, "{\"Key\":\"4\"}")] - [InlineData (KeyCode.Esc, "{\"Key\":\"Esc\"}")] + [InlineData (KeyCode.A, "\"a\"")] + [InlineData ((KeyCode)'â', "\"â\"")] + [InlineData (KeyCode.A | KeyCode.ShiftMask, "\"A\"")] + [InlineData (KeyCode.A | KeyCode.CtrlMask, "\"Ctrl+A\"")] + [InlineData (KeyCode.A | KeyCode.AltMask | KeyCode.CtrlMask, "\"Ctrl+Alt+A\"")] + [InlineData ((KeyCode)'a' | KeyCode.AltMask | KeyCode.CtrlMask, "\"Ctrl+Alt+A\"")] + [InlineData ((KeyCode)'a' | KeyCode.ShiftMask, "\"A\"")] + [InlineData (KeyCode.Delete | KeyCode.AltMask | KeyCode.CtrlMask, "\"Ctrl+Alt+Delete\"")] + [InlineData (KeyCode.D4, "\"4\"")] + [InlineData (KeyCode.Esc, "\"Esc\"")] public void TestKey_Serialize (KeyCode key, string expected) { // Arrange diff --git a/UnitTests/Configuration/SettingsScopeTests.cs b/UnitTests/Configuration/SettingsScopeTests.cs index 571f302c63..09dac60243 100644 --- a/UnitTests/Configuration/SettingsScopeTests.cs +++ b/UnitTests/Configuration/SettingsScopeTests.cs @@ -7,74 +7,74 @@ using System.Threading.Tasks; using static Terminal.Gui.ConfigurationManager; -namespace Terminal.Gui.ConfigurationTests { - public class SettingsScopeTests { - - [Fact] - public void GetHardCodedDefaults_ShouldSetProperties () - { - ConfigurationManager.Reset (); - - Assert.Equal (3, ((Dictionary)ConfigurationManager.Settings ["Themes"].PropertyValue).Count); - - ConfigurationManager.GetHardCodedDefaults (); - Assert.NotEmpty (ConfigurationManager.Themes); - Assert.Equal ("Default", ConfigurationManager.Themes.Theme); - - Assert.True (ConfigurationManager.Settings ["Application.QuitKey"].PropertyValue is Key); - Assert.True (ConfigurationManager.Settings ["Application.AlternateForwardKey"].PropertyValue is Key); - Assert.True (ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue is Key); - Assert.True (ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue is bool); - - Assert.True (ConfigurationManager.Settings ["Theme"].PropertyValue is string); - Assert.Equal ("Default", ConfigurationManager.Settings ["Theme"].PropertyValue as string); - - Assert.True (ConfigurationManager.Settings ["Themes"].PropertyValue is Dictionary); - Assert.Single (((Dictionary)ConfigurationManager.Settings ["Themes"].PropertyValue)); - - } - - [Fact, AutoInitShutdown] - public void Apply_ShouldApplyProperties () - { - // arrange - Assert.Equal (KeyCode.Q | KeyCode.CtrlMask, ((Key)ConfigurationManager.Settings ["Application.QuitKey"].PropertyValue).KeyCode); - Assert.Equal (KeyCode.PageDown | KeyCode.CtrlMask, ((Key)ConfigurationManager.Settings ["Application.AlternateForwardKey"].PropertyValue).KeyCode); - Assert.Equal (KeyCode.PageUp | KeyCode.CtrlMask, ((Key)ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue).KeyCode); - Assert.False ((bool)ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue); - - // act - ConfigurationManager.Settings ["Application.QuitKey"].PropertyValue = new Key (KeyCode.Q); - ConfigurationManager.Settings ["Application.AlternateForwardKey"].PropertyValue = new Key (KeyCode.F); - ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue = new Key (KeyCode.B); - ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue = true; - - ConfigurationManager.Settings.Apply (); - - // assert - Assert.Equal (KeyCode.Q, Application.QuitKey.KeyCode); - Assert.Equal (KeyCode.F, Application.AlternateForwardKey.KeyCode); - Assert.Equal (KeyCode.B, Application.AlternateBackwardKey.KeyCode); - Assert.True (Application.IsMouseDisabled); - } - - [Fact, AutoInitShutdown] - public void CopyUpdatedProperitesFrom_ShouldCopyChangedPropertiesOnly () - { - ConfigurationManager.Settings ["Application.QuitKey"].PropertyValue = new Key (KeyCode.End); ; - - var updatedSettings = new SettingsScope (); - - ///Don't set Quitkey - updatedSettings ["Application.AlternateForwardKey"].PropertyValue = new Key (KeyCode.F); - updatedSettings ["Application.AlternateBackwardKey"].PropertyValue = new Key (KeyCode.B); - updatedSettings ["Application.IsMouseDisabled"].PropertyValue = true; - - ConfigurationManager.Settings.Update (updatedSettings); - Assert.Equal (KeyCode.End, ((Key)ConfigurationManager.Settings ["Application.QuitKey"].PropertyValue).KeyCode); - Assert.Equal (KeyCode.F, ((Key)updatedSettings ["Application.AlternateForwardKey"].PropertyValue).KeyCode); - Assert.Equal (KeyCode.B, ((Key)updatedSettings ["Application.AlternateBackwardKey"].PropertyValue).KeyCode); - Assert.True ((bool)updatedSettings ["Application.IsMouseDisabled"].PropertyValue); - } +namespace Terminal.Gui.ConfigurationTests; + +public class SettingsScopeTests { + [Fact] + public void GetHardCodedDefaults_ShouldSetProperties () + { + Reset (); + + Assert.Equal (3, ((Dictionary)Settings ["Themes"].PropertyValue).Count); + + GetHardCodedDefaults (); + Assert.NotEmpty (Themes); + Assert.Equal ("Default", Themes.Theme); + + Assert.True (Settings ["Application.QuitKey"].PropertyValue is Key); + Assert.True (Settings ["Application.AlternateForwardKey"].PropertyValue is Key); + Assert.True (Settings ["Application.AlternateBackwardKey"].PropertyValue is Key); + Assert.True (Settings ["Application.IsMouseDisabled"].PropertyValue is bool); + + Assert.True (Settings ["Theme"].PropertyValue is string); + Assert.Equal ("Default", Settings ["Theme"].PropertyValue as string); + + Assert.True (Settings ["Themes"].PropertyValue is Dictionary); + Assert.Single ((Dictionary)Settings ["Themes"].PropertyValue); + + } + + [Fact] [AutoInitShutdown] + public void Apply_ShouldApplyProperties () + { + // arrange + Assert.Equal (KeyCode.Q | KeyCode.CtrlMask, ((Key)Settings ["Application.QuitKey"].PropertyValue).KeyCode); + Assert.Equal (KeyCode.PageDown | KeyCode.CtrlMask, ((Key)Settings ["Application.AlternateForwardKey"].PropertyValue).KeyCode); + Assert.Equal (KeyCode.PageUp | KeyCode.CtrlMask, ((Key)Settings ["Application.AlternateBackwardKey"].PropertyValue).KeyCode); + Assert.False ((bool)Settings ["Application.IsMouseDisabled"].PropertyValue); + + // act + Settings ["Application.QuitKey"].PropertyValue = new Key (KeyCode.Q); + Settings ["Application.AlternateForwardKey"].PropertyValue = new Key (KeyCode.F); + Settings ["Application.AlternateBackwardKey"].PropertyValue = new Key (KeyCode.B); + Settings ["Application.IsMouseDisabled"].PropertyValue = true; + + Settings.Apply (); + + // assert + Assert.Equal (KeyCode.Q, Application.QuitKey.KeyCode); + Assert.Equal (KeyCode.F, Application.AlternateForwardKey.KeyCode); + Assert.Equal (KeyCode.B, Application.AlternateBackwardKey.KeyCode); + Assert.True (Application.IsMouseDisabled); + } + + [Fact] [AutoInitShutdown] + public void CopyUpdatedPropertiesFrom_ShouldCopyChangedPropertiesOnly () + { + Settings ["Application.QuitKey"].PropertyValue = new Key (KeyCode.End); + ; + + var updatedSettings = new SettingsScope (); + + ///Don't set Quitkey + updatedSettings ["Application.AlternateForwardKey"].PropertyValue = new Key (KeyCode.F); + updatedSettings ["Application.AlternateBackwardKey"].PropertyValue = new Key (KeyCode.B); + updatedSettings ["Application.IsMouseDisabled"].PropertyValue = true; + + Settings.Update (updatedSettings); + Assert.Equal (KeyCode.End, ((Key)Settings ["Application.QuitKey"].PropertyValue).KeyCode); + Assert.Equal (KeyCode.F, ((Key)updatedSettings ["Application.AlternateForwardKey"].PropertyValue).KeyCode); + Assert.Equal (KeyCode.B, ((Key)updatedSettings ["Application.AlternateBackwardKey"].PropertyValue).KeyCode); + Assert.True ((bool)updatedSettings ["Application.IsMouseDisabled"].PropertyValue); } } \ No newline at end of file diff --git a/UnitTests/Input/KeyTests.cs b/UnitTests/Input/KeyTests.cs index df2e9816c0..018b69b102 100644 --- a/UnitTests/Input/KeyTests.cs +++ b/UnitTests/Input/KeyTests.cs @@ -27,6 +27,126 @@ public void Constructor_WithKey_ShouldSetCorrectKey (KeyCode key) Assert.Equal (key, eventArgs.KeyCode); } + [Theory] + [InlineData ('a', KeyCode.A)] + [InlineData ('A', KeyCode.A | KeyCode.ShiftMask)] + [InlineData ('z', KeyCode.Z)] + [InlineData ('Z', KeyCode.Z | KeyCode.ShiftMask)] + [InlineData (' ', KeyCode.Space)] + [InlineData ('1', KeyCode.D1)] + [InlineData ('!', (KeyCode)'!')] + [InlineData ('\n', KeyCode.Enter)] + [InlineData ('\t', KeyCode.Tab)] + [InlineData ('\r', (KeyCode)13)] + [InlineData ('ó', (KeyCode)'ó')] + [InlineData ('Ó', (KeyCode)'Ó')] + [InlineData ('❿', (KeyCode)'❿')] + [InlineData ('☑', (KeyCode)'☑')] + [InlineData ('英', (KeyCode)'英')] + [InlineData ('{', (KeyCode)'{')] + [InlineData ('\'', (KeyCode)'\'')] + [InlineData ('\xFFFF', (KeyCode)0xFFFF)] + [InlineData ('\x0', (KeyCode)0x0)] + public void Constructor_Char (char ch, KeyCode expectedKeyCode) + { + var key = new Key (ch); + Assert.Equal (expectedKeyCode, key.KeyCode); + } + + + // TryParse + [Theory] + [InlineData ("a", KeyCode.A)] + [InlineData ("Ctrl+A", KeyCode.A | KeyCode.CtrlMask)] + [InlineData ("Alt+A", KeyCode.A | KeyCode.AltMask)] + [InlineData ("Shift+A", KeyCode.A | KeyCode.ShiftMask)] + [InlineData ("A", KeyCode.A | KeyCode.ShiftMask)] + [InlineData ("â", (KeyCode)'â')] + [InlineData ("Shift+â", (KeyCode)'â' | KeyCode.ShiftMask)] + [InlineData ("Shift+Â", (KeyCode)'Â' | KeyCode.ShiftMask)] + [InlineData ("Ctrl+Shift+CursorUp", KeyCode.ShiftMask | KeyCode.CtrlMask | KeyCode.CursorUp)] + [InlineData ("Ctrl+Alt+Shift+CursorUp", KeyCode.ShiftMask | KeyCode.CtrlMask | KeyCode.AltMask | KeyCode.CursorUp)] + [InlineData ("ctrl+alt+shift+cursorup", KeyCode.ShiftMask | KeyCode.CtrlMask | KeyCode.AltMask | KeyCode.CursorUp)] + [InlineData ("CTRL+ALT+SHIFT+CURSORUP", KeyCode.ShiftMask | KeyCode.CtrlMask | KeyCode.AltMask | KeyCode.CursorUp)] + [InlineData ("Ctrl+Alt+Shift+Delete", KeyCode.ShiftMask | KeyCode.CtrlMask | KeyCode.AltMask | KeyCode.Delete)] + [InlineData ("Ctrl+Alt+Shift+Enter", KeyCode.ShiftMask | KeyCode.CtrlMask | KeyCode.AltMask | KeyCode.Enter)] + [InlineData ("Tab", KeyCode.Tab)] + [InlineData ("Shift+Tab", KeyCode.Tab | KeyCode.ShiftMask)] + [InlineData ("Ctrl+Tab", KeyCode.Tab | KeyCode.CtrlMask)] + [InlineData ("Alt+Tab", KeyCode.Tab | KeyCode.AltMask)] + [InlineData ("Ctrl+Shift+Tab", KeyCode.Tab | KeyCode.ShiftMask | KeyCode.CtrlMask)] + [InlineData ("Ctrl+Alt+Tab", KeyCode.Tab | KeyCode.AltMask | KeyCode.CtrlMask)] + [InlineData ("", KeyCode.Null)] + [InlineData (" ", KeyCode.Space)] + [InlineData ("Shift+ ", KeyCode.Space | KeyCode.ShiftMask)] + [InlineData ("Ctrl+ ", KeyCode.Space | KeyCode.CtrlMask)] + [InlineData ("Alt+ ", KeyCode.Space | KeyCode.AltMask)] + [InlineData ("F1", KeyCode.F1)] + [InlineData ("0", KeyCode.D0)] + [InlineData ("9", KeyCode.D9)] + [InlineData ("D0", KeyCode.D0)] + [InlineData ("65", KeyCode.A | KeyCode.ShiftMask)] + [InlineData ("97", KeyCode.A)] + [InlineData ("Shift", KeyCode.ShiftKey)] + [InlineData ("Ctrl", KeyCode.CtrlKey)] + [InlineData ("Ctrl-A", KeyCode.A | KeyCode.CtrlMask)] + [InlineData ("Alt-A", KeyCode.A | KeyCode.AltMask)] + [InlineData ("A-Ctrl", KeyCode.A | KeyCode.CtrlMask)] + [InlineData ("Alt-A-Ctrl", KeyCode.A | KeyCode.CtrlMask | KeyCode.AltMask)] + public void Constructor_String_Valid (string keyString, Key expected) + { + Key key = new Key (keyString); + Assert.Equal (((Key)expected).ToString (), key.ToString ()); + } + + [Theory] + [InlineData("Barf")] + public void Constructor_String_Invalid_Throws (string keyString) + { + Assert.Throws (() => new Key (keyString)); + } + + [Theory] + [InlineData ('a', KeyCode.A)] + [InlineData ('A', KeyCode.A | KeyCode.ShiftMask)] + [InlineData ('z', KeyCode.Z)] + [InlineData ('Z', KeyCode.Z | KeyCode.ShiftMask)] + [InlineData (' ', KeyCode.Space)] + [InlineData ('1', KeyCode.D1)] + [InlineData ('!', (KeyCode)'!')] + [InlineData ('\n', KeyCode.Enter)] + [InlineData ('\t', KeyCode.Tab)] + [InlineData ('\r', (KeyCode)13)] + [InlineData ('ó', (KeyCode)'ó')] + [InlineData ('Ó', (KeyCode)'Ó')] + [InlineData ('❿', (KeyCode)'❿')] + [InlineData ('☑', (KeyCode)'☑')] + [InlineData ('英', (KeyCode)'英')] + [InlineData ('{', (KeyCode)'{')] + [InlineData ('\'', (KeyCode)'\'')] + [InlineData ('\xFFFF', (KeyCode)0xFFFF)] + [InlineData ('\x0', (KeyCode)0x0)] + public void Cast_Char_To_Key (char ch, KeyCode expectedKeyCode) + { + var key = (Key)ch; + Assert.Equal (expectedKeyCode, key.KeyCode); + } + + // string cast operators + [Fact] + public void Cast_String_To_Key () + { + var key = (Key)"Ctrl+Q"; + Assert.Equal (KeyCode.Q | KeyCode.CtrlMask, key.KeyCode); + } + + [Fact] + public void Cast_Key_ToString () + { + var str = (string)Key.Q.WithCtrl; + Assert.Equal ("Ctrl+Q", str); + } + // IsValid [Theory] [InlineData (KeyCode.A, true)] @@ -64,6 +184,28 @@ public void Cast_KeyCode_To_Key (KeyCode cdk, Key expected) Assert.Equal (expected.ToString (), key.ToString ()); } + [Fact] + public void Standard_Keys_Always_New () + { + // Make two local keys, and grab Key.A, which is a reference to a singleton. + Key aKey1 = Key.A; + Key aKey2 = Key.A; + + // Assert the starting state that is expected + Assert.False (aKey1.Handled); + Assert.False (aKey2.Handled); + Assert.False (Key.A.Handled); + + // Now set Handled true on one of our local keys + aKey1.Handled = true; + + // Assert the newly-expected case + // The last two assertions will fail, because we have actually modified a singleton + Assert.True (aKey1.Handled); + Assert.False (aKey2.Handled); + Assert.False (Key.A.Handled); + } + [Theory] [InlineData ((KeyCode)'a', true)] [InlineData ((KeyCode)'a' | KeyCode.ShiftMask, true)] @@ -96,7 +238,7 @@ public void IsKeyCodeAtoZ (KeyCode key, bool expected) [InlineData ((KeyCode)'ç' | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '\0')] [InlineData ((KeyCode)'a', 97)] // 97 or Key.Space | Key.A [InlineData ((KeyCode)'A', 97)] // 65 or equivalent to Key.A, but A-Z are mapped to lower case by drivers - //[InlineData (Key.A, 97)] // 65 equivalent to (Key)'A', but A-Z are mapped to lower case by drivers + //[InlineData (Key.A, 97)] // 65 equivalent to (Key)'A', but A-Z are mapped to lower case by drivers [InlineData (KeyCode.ShiftMask | KeyCode.A, 65)] [InlineData (KeyCode.CtrlMask | KeyCode.A, '\0')] [InlineData (KeyCode.AltMask | KeyCode.A, '\0')] @@ -263,14 +405,6 @@ public void ToString_ShouldReturnReadableString () [InlineData (KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.Null, "Null")] [InlineData (KeyCode.AltMask | KeyCode.CtrlMask | KeyCode.Null, "Null")] [InlineData (KeyCode.ShiftMask | KeyCode.CtrlMask | KeyCode.AltMask | KeyCode.Null, "Null")] - [InlineData (KeyCode.Null, "Null")] - [InlineData (KeyCode.ShiftMask | KeyCode.Null, "Null")] - [InlineData (KeyCode.CtrlMask | KeyCode.Null, "Null")] - [InlineData (KeyCode.AltMask | KeyCode.Null, "Null")] - [InlineData (KeyCode.ShiftMask | KeyCode.CtrlMask | KeyCode.Null, "Null")] - [InlineData (KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.Null, "Null")] - [InlineData (KeyCode.AltMask | KeyCode.CtrlMask | KeyCode.Null, "Null")] - [InlineData (KeyCode.ShiftMask | KeyCode.CtrlMask | KeyCode.AltMask | KeyCode.Null, "Null")] [InlineData (KeyCode.AltKey, "AltKey")] [InlineData (KeyCode.CtrlKey, "CtrlKey")] [InlineData (KeyCode.ShiftKey, "ShiftKey")] diff --git a/UnitTests/Views/AppendAutocompleteTests.cs b/UnitTests/Views/AppendAutocompleteTests.cs index 4795520d3d..643b8fdbc8 100644 --- a/UnitTests/Views/AppendAutocompleteTests.cs +++ b/UnitTests/Views/AppendAutocompleteTests.cs @@ -26,7 +26,7 @@ public void TestAutoAppend_ShowThenAccept_MatchCase () tf.PositionCursor (); TestHelpers.AssertDriverContentsAre ("", output); - tf.NewKeyDownEvent ('f'); + tf.NewKeyDownEvent (new ('f')); tf.Draw (); tf.PositionCursor ();