diff --git a/Terminal.Gui/Configuration/ColorJsonConverter.cs b/Terminal.Gui/Configuration/ColorJsonConverter.cs
index aae59b2d7..6da4490d9 100644
--- a/Terminal.Gui/Configuration/ColorJsonConverter.cs
+++ b/Terminal.Gui/Configuration/ColorJsonConverter.cs
@@ -4,7 +4,16 @@ using ColorHelper;
namespace Terminal.Gui;
-/// Json converter for the class.
+///
+/// Json converter for the class.
+///
+/// Serialization outputs a string with the color name if the color matches a name in
+/// or the "#RRGGBB" hexadecimal representation (e.g. "#FF0000" for red).
+///
+///
+/// Deserialization formats supported are "#RGB", "#RRGGBB", "#ARGB", "#AARRGGBB", "rgb(r,g,b)",
+/// "rgb(r,g,b,a)", "rgba(r,g,b)", "rgba(r,g,b,a)", or any W3C color name.
+///
internal class ColorJsonConverter : JsonConverter
{
private static ColorJsonConverter _instance;
diff --git a/Terminal.Gui/Drawing/Color.Formatting.cs b/Terminal.Gui/Drawing/Color.Formatting.cs
index 65cad5626..dd775fe04 100644
--- a/Terminal.Gui/Drawing/Color.Formatting.cs
+++ b/Terminal.Gui/Drawing/Color.Formatting.cs
@@ -501,7 +501,7 @@ public readonly partial record struct Color
///
///
/// The text to analyze. Formats supported are "#RGB", "#RRGGBB", "#ARGB", "#AARRGGBB", "rgb(r,g,b)",
- /// "rgb(r,g,b,a)", "rgba(r,g,b)", "rgba(r,g,b,a)", and any of the string
+ /// "rgb(r,g,b,a)", "rgba(r,g,b)", "rgba(r,g,b,a)", and any W3C color name."/> string
/// values.
///
///
diff --git a/Terminal.Gui/Input/Key.cs b/Terminal.Gui/Input/Key.cs
index 5c92cc489..11f8d938e 100644
--- a/Terminal.Gui/Input/Key.cs
+++ b/Terminal.Gui/Input/Key.cs
@@ -1,6 +1,5 @@
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
-using System.Text.Json.Serialization;
namespace Terminal.Gui;
@@ -80,7 +79,7 @@ public class Key : EventArgs, IEquatable
public Key (KeyCode k) { KeyCode = k; }
///
- /// Copy constructor.
+ /// Copy constructor.
///
/// The Key to copy
public Key (Key key)
@@ -155,18 +154,13 @@ public class Key : EventArgs, IEquatable
///
public Rune AsRune => ToRune (KeyCode);
- private bool _handled = false;
-
///
/// Indicates if the current Key event has already been processed and the driver should stop notifying any other
- /// event subscriber. It's important to set this value to true specially when updating any View's layout from inside the
+ /// event subscriber. It's important to set this value to true specially when updating any View's layout from inside
+ /// the
/// subscriber method.
///
- public bool Handled
- {
- get => _handled;
- set => _handled = value;
- }
+ public bool Handled { get; set; }
/// Gets a value indicating whether the Alt key was pressed (real or synthesized)
/// if is alternate; otherwise, .
@@ -339,11 +333,11 @@ public class Key : EventArgs, IEquatable
switch (baseKey)
{
case >= KeyCode.A and <= KeyCode.Z when !key.HasFlag (KeyCode.ShiftMask):
- return new Rune ((uint)(baseKey + 32));
+ return new ((uint)(baseKey + 32));
case >= KeyCode.A and <= KeyCode.Z when key.HasFlag (KeyCode.ShiftMask):
- return new Rune ((uint)baseKey);
+ return new ((uint)baseKey);
case > KeyCode.Null and < KeyCode.A:
- return new Rune ((uint)baseKey);
+ return new ((uint)baseKey);
}
if (Enum.IsDefined (typeof (KeyCode), baseKey))
@@ -351,7 +345,7 @@ public class Key : EventArgs, IEquatable
return default (Rune);
}
- return new Rune ((uint)baseKey);
+ return new ((uint)baseKey);
}
#region Operators
@@ -381,17 +375,17 @@ public class Key : EventArgs, IEquatable
/// Cast to a .
///
- public static implicit operator Key (KeyCode keyCode) { return new Key (keyCode); }
+ public static implicit operator Key (KeyCode keyCode) { return new (keyCode); }
/// Cast to a .
/// See for more information.
///
- public static implicit operator Key (char ch) { return new Key (ch); }
+ public static implicit operator Key (char ch) { return new (ch); }
/// Cast to a .
/// See for more information.
///
- public static implicit operator Key (string str) { return new Key (str); }
+ public static implicit operator Key (string str) { return new (str); }
/// Cast a to a .
/// See for more information.
@@ -399,10 +393,7 @@ public class Key : EventArgs, IEquatable
public static implicit operator string (Key key) { return key.ToString (); }
///
- public override bool Equals (object obj)
- {
- return obj is Key k && k.KeyCode == KeyCode && k.Handled == Handled;
- }
+ public override bool Equals (object obj) { return obj is Key k && k.KeyCode == KeyCode && k.Handled == Handled; }
bool IEquatable.Equals (Key other) { return Equals (other); }
@@ -568,7 +559,10 @@ public class Key : EventArgs, IEquatable
/// Converts the provided string to a new instance.
///
/// The text to analyze. Formats supported are "Ctrl+X", "Alt+X", "Shift+X", "Ctrl+Alt+X",
- /// "Ctrl+Shift+X", "Alt+Shift+X", "Ctrl+Alt+Shift+X", and "X".
+ /// "Ctrl+Shift+X", "Alt+Shift+X", "Ctrl+Alt+Shift+X", "X", and "120" (Unicode codepoint).
+ ///
+ /// The separator can be any character, not just (e.g. "Ctrl@Alt@X").
+ ///
///
/// The parsed value.
/// A boolean value indicating whether parsing was successful.
@@ -577,38 +571,88 @@ public class Key : EventArgs, IEquatable
{
if (string.IsNullOrEmpty (text))
{
- key = Key.Empty;
+ key = Empty;
return true;
}
+ switch (text)
+ {
+ case "Ctrl":
+ key = KeyCode.CtrlMask;
+
+ return true;
+ case "Alt":
+ key = KeyCode.AltMask;
+
+ return true;
+ case "Shift":
+ key = KeyCode.ShiftMask;
+
+ return true;
+ }
+
key = null;
- // Split the string into parts
- string [] parts = text.Split ('+', '-', (char)Separator.Value);
+ Rune separator = Separator;
- if (parts.Length is 0 or > 4 || parts.Any (string.IsNullOrEmpty))
+ // Perhaps the separator was written using a different Key.Separator? Does the string
+ // start with "Ctrl", "Alt" or "Shift"? If so, get the char after the modifier string and use that as the separator.
+ if (text.StartsWith ("Ctrl", StringComparison.InvariantCultureIgnoreCase))
{
+ separator = (Rune)text [4];
+ }
+ else if (text.StartsWith ("Alt", StringComparison.InvariantCultureIgnoreCase))
+ {
+ separator = (Rune)text [3];
+ }
+ else if (text.StartsWith ("Shift", StringComparison.InvariantCultureIgnoreCase))
+ {
+ separator = (Rune)text [5];
+ }
+ else if (text.EndsWith ("Ctrl", StringComparison.InvariantCultureIgnoreCase))
+ {
+ separator = (Rune)text [^5];
+ }
+ else if (text.EndsWith ("Alt", StringComparison.InvariantCultureIgnoreCase))
+ {
+ separator = (Rune)text [^4];
+ }
+ else if (text.EndsWith ("Shift", StringComparison.InvariantCultureIgnoreCase))
+ {
+ separator = (Rune)text [^6];
+ }
+
+ // Split the string into parts using the set Separator
+ string [] parts = text.Split ((char)separator.Value);
+
+ if (parts.Length is > 4)
+ {
+ // Invalid
return false;
}
- // if it's just a shift key
- if (parts.Length == 1)
+ // e.g. "Ctrl++"
+ if ((Rune)text [^1] != separator && parts.Any (string.IsNullOrEmpty))
{
- switch (parts [0])
+ // Invalid
+ return false;
+ }
+
+ if ((Rune)text [^1] == separator)
+ {
+ parts [^1] = separator.Value.ToString ();
+ key = (char)separator.Value;
+ }
+
+ if (separator != Separator && (parts.Length is 1 || (key is { } && parts.Length is 2)))
+ {
+ parts = text.Split ((char)separator.Value);
+
+ if (parts.Length is 0 or > 4 || parts.Any (string.IsNullOrEmpty))
{
- case "Ctrl":
- key = KeyCode.CtrlMask;
-
- return true;
- case "Alt":
- key = KeyCode.AltMask;
-
- return true;
- case "Shift":
- key = KeyCode.ShiftMask;
-
- return true;
+ // Invalid
+ return false;
}
}
@@ -649,12 +693,12 @@ public class Key : EventArgs, IEquatable
{
if (parsedKeyCode is >= KeyCode.A and <= KeyCode.Z && modifiers == 0)
{
- key = new Key (parsedKeyCode | KeyCode.ShiftMask);
+ key = new (parsedKeyCode | KeyCode.ShiftMask);
return true;
}
- key = new Key (parsedKeyCode | modifiers);
+ key = new (parsedKeyCode | modifiers);
return true;
}
@@ -664,7 +708,8 @@ public class Key : EventArgs, IEquatable
{
keyCode = keyCode & ~KeyCode.Space;
}
- key = new Key (keyCode | modifiers);
+
+ key = new (keyCode | modifiers);
return true;
}
@@ -675,7 +720,7 @@ public class Key : EventArgs, IEquatable
{
if (parsedKeyCode is >= KeyCode.A and <= KeyCode.Z && modifiers == 0)
{
- key = new Key (parsedKeyCode | KeyCode.ShiftMask);
+ key = new (parsedKeyCode | KeyCode.ShiftMask);
return true;
}
@@ -684,7 +729,8 @@ public class Key : EventArgs, IEquatable
{
parsedKeyCode = parsedKeyCode & ~KeyCode.Space;
}
- key = new Key (parsedKeyCode | modifiers);
+
+ key = new (parsedKeyCode | modifiers);
return true;
}
@@ -705,12 +751,12 @@ public class Key : EventArgs, IEquatable
if ((KeyCode)parsedInt is >= KeyCode.A and <= KeyCode.Z && modifiers == 0)
{
- key = new Key ((KeyCode)parsedInt | KeyCode.ShiftMask);
+ key = new ((KeyCode)parsedInt | KeyCode.ShiftMask);
return true;
}
- key = new Key ((KeyCode)parsedInt);
+ key = new ((KeyCode)parsedInt);
return true;
}
@@ -722,7 +768,7 @@ public class Key : EventArgs, IEquatable
if (GetIsKeyCodeAtoZ (parsedKeyCode))
{
- key = new Key (parsedKeyCode | (modifiers & ~KeyCode.Space));
+ key = new (parsedKeyCode | (modifiers & ~KeyCode.Space));
return true;
}
diff --git a/UnitTests/Configuration/AttributeJsonConverterTests.cs b/UnitTests/Configuration/AttributeJsonConverterTests.cs
new file mode 100644
index 000000000..8487e0271
--- /dev/null
+++ b/UnitTests/Configuration/AttributeJsonConverterTests.cs
@@ -0,0 +1,32 @@
+using System.Text.Json;
+
+namespace Terminal.Gui.ConfigurationTests;
+
+public class AttributeJsonConverterTests
+{
+ [Fact]
+ public void TestDeserialize ()
+ {
+ // Test deserializing from human-readable color names
+ var json = "{\"Foreground\":\"Blue\",\"Background\":\"Green\"}";
+ var attribute = JsonSerializer.Deserialize (json, ConfigurationManagerTests._jsonOptions);
+ Assert.Equal (Color.Blue, attribute.Foreground.GetClosestNamedColor16 ());
+ Assert.Equal (Color.Green, attribute.Background.GetClosestNamedColor16 ());
+
+ // Test deserializing from RGB values
+ json = "{\"Foreground\":\"rgb(255,0,0)\",\"Background\":\"rgb(0,255,0)\"}";
+ attribute = JsonSerializer.Deserialize (json, ConfigurationManagerTests._jsonOptions);
+ Assert.Equal (Color.Red, attribute.Foreground.GetClosestNamedColor16 ());
+ Assert.Equal (Color.BrightGreen, attribute.Background.GetClosestNamedColor16 ());
+ }
+
+ [Fact]
+ [AutoInitShutdown]
+ public void TestSerialize ()
+ {
+ // Test serializing to human-readable color names
+ var attribute = new Attribute (Color.Blue, Color.Green);
+ string json = JsonSerializer.Serialize (attribute, ConfigurationManagerTests._jsonOptions);
+ Assert.Equal ("{\"Foreground\":\"Blue\",\"Background\":\"Green\"}", json);
+ }
+}
\ No newline at end of file
diff --git a/UnitTests/Configuration/ColorJsonConverterTests.cs b/UnitTests/Configuration/ColorJsonConverterTests.cs
new file mode 100644
index 000000000..ed06432d7
--- /dev/null
+++ b/UnitTests/Configuration/ColorJsonConverterTests.cs
@@ -0,0 +1,180 @@
+using System.Text;
+using System.Text.Json;
+
+namespace Terminal.Gui.ConfigurationTests;
+
+public class ColorJsonConverterTests
+{
+ [Theory]
+ [InlineData ("\"#000000\"", 0, 0, 0)]
+ public void DeserializesFromHexCode (string hexCode, int r, int g, int b)
+ {
+ // Arrange
+ var expected = new Color (r, g, b);
+
+ // Act
+ var actual = JsonSerializer.Deserialize (
+ hexCode,
+ new JsonSerializerOptions { Converters = { new ColorJsonConverter () } }
+ );
+
+ //Assert
+ Assert.Equal (expected, actual);
+ }
+
+ [Theory]
+ [InlineData ("\"rgb(0,0,0)\"", 0, 0, 0)]
+ public void DeserializesFromRgb (string rgb, int r, int g, int b)
+ {
+ // Arrange
+ var expected = new Color (r, g, b);
+
+ // Act
+ var actual = JsonSerializer.Deserialize (
+ rgb,
+ new JsonSerializerOptions { Converters = { new ColorJsonConverter () } }
+ );
+
+ //Assert
+ Assert.Equal (expected, actual);
+ }
+
+ [Theory]
+ [InlineData (ColorName16.Black, "Black")]
+ [InlineData (ColorName16.Blue, "Blue")]
+ [InlineData (ColorName16.Green, "Green")]
+ [InlineData (ColorName16.Cyan, "Cyan")]
+ [InlineData (ColorName16.Gray, "Gray")]
+ [InlineData (ColorName16.Red, "Red")]
+ [InlineData (ColorName16.Magenta, "Magenta")]
+ [InlineData (ColorName16.Yellow, "Yellow")]
+ [InlineData (ColorName16.DarkGray, "DarkGray")]
+ [InlineData (ColorName16.BrightBlue, "BrightBlue")]
+ [InlineData (ColorName16.BrightGreen, "BrightGreen")]
+ [InlineData (ColorName16.BrightCyan, "BrightCyan")]
+ [InlineData (ColorName16.BrightRed, "BrightRed")]
+ [InlineData (ColorName16.BrightMagenta, "BrightMagenta")]
+ [InlineData (ColorName16.BrightYellow, "BrightYellow")]
+ [InlineData (ColorName16.White, "White")]
+ public void SerializesColorName16ValuesAsStrings (ColorName16 colorName, string expectedJson)
+ {
+ var converter = new ColorJsonConverter ();
+ var options = new JsonSerializerOptions { Converters = { converter } };
+
+ string serialized = JsonSerializer.Serialize (new Color (colorName), options);
+
+ Assert.Equal ($"\"{expectedJson}\"", serialized);
+ }
+
+ [Theory]
+ [InlineData (1, 0, 0, "\"#010000\"")]
+ [InlineData (0, 0, 1, "\"#000001\"")]
+ public void SerializesToHexCode (int r, int g, int b, string expected)
+ {
+ // Arrange
+
+ // Act
+ string actual = JsonSerializer.Serialize (
+ new Color (r, g, b),
+ new JsonSerializerOptions { Converters = { new ColorJsonConverter () } }
+ );
+
+ //Assert
+ Assert.Equal (expected, actual);
+ }
+
+ [Theory]
+ [InlineData ("Black", Color.Black)]
+ [InlineData ("Blue", Color.Blue)]
+ [InlineData ("BrightBlue", Color.BrightBlue)]
+ [InlineData ("BrightCyan", Color.BrightCyan)]
+ [InlineData ("BrightGreen", Color.BrightGreen)]
+ [InlineData ("BrightMagenta", Color.BrightMagenta)]
+ [InlineData ("BrightRed", Color.BrightRed)]
+ [InlineData ("BrightYellow", Color.BrightYellow)]
+ [InlineData ("Yellow", Color.Yellow)]
+ [InlineData ("Cyan", Color.Cyan)]
+ [InlineData ("DarkGray", Color.DarkGray)]
+ [InlineData ("Gray", Color.Gray)]
+ [InlineData ("Green", Color.Green)]
+ [InlineData ("Magenta", Color.Magenta)]
+ [InlineData ("Red", Color.Red)]
+ [InlineData ("White", Color.White)]
+ public void TestColorDeserializationFromHumanReadableColorName16 (string colorName, ColorName16 expectedColor)
+ {
+ // Arrange
+ var json = $"\"{colorName}\"";
+
+ // Act
+ var actualColor = JsonSerializer.Deserialize (json, ConfigurationManagerTests._jsonOptions);
+
+ // Assert
+ Assert.Equal (new Color (expectedColor), actualColor);
+ }
+
+ [Fact]
+ public void TestDeserializeColor_Black ()
+ {
+ // Arrange
+ var json = "\"Black\"";
+ var expectedColor = new Color ("Black");
+
+ // Act
+ var color = JsonSerializer.Deserialize (
+ json,
+ new JsonSerializerOptions { Converters = { new ColorJsonConverter () } }
+ );
+
+ // Assert
+ Assert.Equal (expectedColor, color);
+ }
+
+ [Fact]
+ public void TestDeserializeColor_BrightRed ()
+ {
+ // Arrange
+ var json = "\"BrightRed\"";
+ var expectedColor = Color.BrightRed;
+
+ // Act
+ var color = JsonSerializer.Deserialize (
+ json,
+ new JsonSerializerOptions { Converters = { new ColorJsonConverter () } }
+ );
+
+ // Assert
+ Assert.Equal (expectedColor, color);
+ }
+
+ [Fact]
+ public void TestSerializeColor_Black ()
+ {
+ // Arrange
+ var expectedJson = "\"Black\"";
+
+ // Act
+ string json = JsonSerializer.Serialize (
+ new Color (Color.Black),
+ new JsonSerializerOptions { Converters = { new ColorJsonConverter () } }
+ );
+
+ // Assert
+ Assert.Equal (expectedJson, json);
+ }
+
+ [Fact]
+ public void TestSerializeColor_BrightRed ()
+ {
+ // Arrange
+ var expectedJson = "\"BrightRed\"";
+
+ // Act
+ string json = JsonSerializer.Serialize (
+ new Color (Color.BrightRed),
+ new JsonSerializerOptions { Converters = { new ColorJsonConverter () } }
+ );
+
+ // Assert
+ Assert.Equal (expectedJson, json);
+ }
+}
\ No newline at end of file
diff --git a/UnitTests/Configuration/ColorSchemeJsonConverterTests.cs b/UnitTests/Configuration/ColorSchemeJsonConverterTests.cs
new file mode 100644
index 000000000..2fd50838b
--- /dev/null
+++ b/UnitTests/Configuration/ColorSchemeJsonConverterTests.cs
@@ -0,0 +1,58 @@
+using System.Text.Json;
+
+namespace Terminal.Gui.ConfigurationTests;
+
+public class ColorSchemeJsonConverterTests
+{
+ //string json = @"
+ // {
+ // ""ColorSchemes"": {
+ // ""Base"": {
+ // ""normal"": {
+ // ""foreground"": ""White"",
+ // ""background"": ""Blue""
+ // },
+ // ""focus"": {
+ // ""foreground"": ""Black"",
+ // ""background"": ""Gray""
+ // },
+ // ""hotNormal"": {
+ // ""foreground"": ""BrightCyan"",
+ // ""background"": ""Blue""
+ // },
+ // ""hotFocus"": {
+ // ""foreground"": ""BrightBlue"",
+ // ""background"": ""Gray""
+ // },
+ // ""disabled"": {
+ // ""foreground"": ""DarkGray"",
+ // ""background"": ""Blue""
+ // }
+ // }
+ // }
+ // }";
+ [Fact]
+ [AutoInitShutdown]
+ public void TestColorSchemesSerialization ()
+ {
+ // Arrange
+ var expectedColorScheme = new ColorScheme
+ {
+ Normal = new Attribute (Color.White, Color.Blue),
+ Focus = new Attribute (Color.Black, Color.Gray),
+ HotNormal = new Attribute (Color.BrightCyan, Color.Blue),
+ HotFocus = new Attribute (Color.BrightBlue, Color.Gray),
+ Disabled = new Attribute (Color.DarkGray, Color.Blue)
+ };
+
+ string serializedColorScheme =
+ JsonSerializer.Serialize (expectedColorScheme, ConfigurationManagerTests._jsonOptions);
+
+ // Act
+ var actualColorScheme =
+ JsonSerializer.Deserialize (serializedColorScheme, ConfigurationManagerTests._jsonOptions);
+
+ // Assert
+ Assert.Equal (expectedColorScheme, actualColorScheme);
+ }
+}
\ No newline at end of file
diff --git a/UnitTests/Configuration/JsonConverterTests.cs b/UnitTests/Configuration/JsonConverterTests.cs
deleted file mode 100644
index 2d1262647..000000000
--- a/UnitTests/Configuration/JsonConverterTests.cs
+++ /dev/null
@@ -1,355 +0,0 @@
-using System.Text;
-using System.Text.Encodings.Web;
-using System.Text.Json;
-using System.Text.Unicode;
-
-namespace Terminal.Gui.ConfigurationTests;
-
-public class ColorJsonConverterTests
-{
- [Theory]
- [InlineData ("\"#000000\"", 0, 0, 0)]
- public void DeserializesFromHexCode (string hexCode, int r, int g, int b)
- {
- // Arrange
- var expected = new Color (r, g, b);
-
- // Act
- var actual = JsonSerializer.Deserialize (
- hexCode,
- new JsonSerializerOptions { Converters = { new ColorJsonConverter () } }
- );
-
- //Assert
- Assert.Equal (expected, actual);
- }
-
- [Theory]
- [InlineData ("\"rgb(0,0,0)\"", 0, 0, 0)]
- public void DeserializesFromRgb (string rgb, int r, int g, int b)
- {
- // Arrange
- var expected = new Color (r, g, b);
-
- // Act
- var actual = JsonSerializer.Deserialize (
- rgb,
- new JsonSerializerOptions { Converters = { new ColorJsonConverter () } }
- );
-
- //Assert
- Assert.Equal (expected, actual);
- }
-
- [Theory]
- [InlineData (ColorName16.Black, "Black")]
- [InlineData (ColorName16.Blue, "Blue")]
- [InlineData (ColorName16.Green, "Green")]
- [InlineData (ColorName16.Cyan, "Cyan")]
- [InlineData (ColorName16.Gray, "Gray")]
- [InlineData (ColorName16.Red, "Red")]
- [InlineData (ColorName16.Magenta, "Magenta")]
- [InlineData (ColorName16.Yellow, "Yellow")]
- [InlineData (ColorName16.DarkGray, "DarkGray")]
- [InlineData (ColorName16.BrightBlue, "BrightBlue")]
- [InlineData (ColorName16.BrightGreen, "BrightGreen")]
- [InlineData (ColorName16.BrightCyan, "BrightCyan")]
- [InlineData (ColorName16.BrightRed, "BrightRed")]
- [InlineData (ColorName16.BrightMagenta, "BrightMagenta")]
- [InlineData (ColorName16.BrightYellow, "BrightYellow")]
- [InlineData (ColorName16.White, "White")]
- public void SerializesEnumValuesAsStrings (ColorName16 colorName, string expectedJson)
- {
- var converter = new ColorJsonConverter ();
- var options = new JsonSerializerOptions { Converters = { converter } };
-
- string serialized = JsonSerializer.Serialize (new Color (colorName), options);
-
- Assert.Equal ($"\"{expectedJson}\"", serialized);
- }
-
- [Theory (Skip = "Not anymore. If a W3C color matches, that's used")]
- [InlineData (0, 0, 0, "\"#000000\"")]
- [InlineData (0, 0, 1, "\"#000001\"")]
- public void SerializesToHexCode (int r, int g, int b, string expected)
- {
- // Arrange
-
- // Act
- string actual = JsonSerializer.Serialize (
- new Color (r, g, b),
- new JsonSerializerOptions { Converters = { new ColorJsonConverter () } }
- );
-
- //Assert
- Assert.Equal (expected, actual);
- }
-
- [Theory]
- [InlineData ("Black", Color.Black)]
- [InlineData ("Blue", Color.Blue)]
- [InlineData ("BrightBlue", Color.BrightBlue)]
- [InlineData ("BrightCyan", Color.BrightCyan)]
- [InlineData ("BrightGreen", Color.BrightGreen)]
- [InlineData ("BrightMagenta", Color.BrightMagenta)]
- [InlineData ("BrightRed", Color.BrightRed)]
- [InlineData ("BrightYellow", Color.BrightYellow)]
- [InlineData ("Yellow", Color.Yellow)]
- [InlineData ("Cyan", Color.Cyan)]
- [InlineData ("DarkGray", Color.DarkGray)]
- [InlineData ("Gray", Color.Gray)]
- [InlineData ("Green", Color.Green)]
- [InlineData ("Magenta", Color.Magenta)]
- [InlineData ("Red", Color.Red)]
- [InlineData ("White", Color.White)]
- public void TestColorDeserializationFromHumanReadableColorName16 (string colorName, ColorName16 expectedColor)
- {
- // Arrange
- var json = $"\"{colorName}\"";
-
- // Act
- var actualColor = JsonSerializer.Deserialize (json, ConfigurationManagerTests._jsonOptions);
-
- // Assert
- Assert.Equal (new Color (expectedColor), actualColor);
- }
-
- [Fact]
- public void TestDeserializeColor_Black ()
- {
- // Arrange
- var json = "\"Black\"";
- var expectedColor = new Color ("Black");
-
- // Act
- var color = JsonSerializer.Deserialize (
- json,
- new JsonSerializerOptions { Converters = { new ColorJsonConverter () } }
- );
-
- // Assert
- Assert.Equal (expectedColor, color);
- }
-
- [Fact]
- public void TestDeserializeColor_BrightRed ()
- {
- // Arrange
- var json = "\"BrightRed\"";
- var expectedColor = Color.BrightRed;
-
- // Act
- var color = JsonSerializer.Deserialize (
- json,
- new JsonSerializerOptions { Converters = { new ColorJsonConverter () } }
- );
-
- // Assert
- Assert.Equal (expectedColor, color);
- }
-
- [Fact]
- public void TestSerializeColor_Black ()
- {
- // Arrange
- var expectedJson = "\"Black\"";
-
- // Act
- string json = JsonSerializer.Serialize (
- new Color (Color.Black),
- new JsonSerializerOptions { Converters = { new ColorJsonConverter () } }
- );
-
- // Assert
- Assert.Equal (expectedJson, json);
- }
-
- [Fact]
- public void TestSerializeColor_BrightRed ()
- {
- // Arrange
- var expectedJson = "\"BrightRed\"";
-
- // Act
- string json = JsonSerializer.Serialize (
- new Color (Color.BrightRed),
- new JsonSerializerOptions { Converters = { new ColorJsonConverter () } }
- );
-
- // Assert
- Assert.Equal (expectedJson, json);
- }
-}
-
-public class AttributeJsonConverterTests
-{
- [Fact]
- public void TestDeserialize ()
- {
- // Test deserializing from human-readable color names
- var json = "{\"Foreground\":\"Blue\",\"Background\":\"Green\"}";
- var attribute = JsonSerializer.Deserialize (json, ConfigurationManagerTests._jsonOptions);
- Assert.Equal (Color.Blue, attribute.Foreground.GetClosestNamedColor16 ());
- Assert.Equal (Color.Green, attribute.Background.GetClosestNamedColor16 ());
-
- // Test deserializing from RGB values
- json = "{\"Foreground\":\"rgb(255,0,0)\",\"Background\":\"rgb(0,255,0)\"}";
- attribute = JsonSerializer.Deserialize (json, ConfigurationManagerTests._jsonOptions);
- Assert.Equal (Color.Red, attribute.Foreground.GetClosestNamedColor16 ());
- Assert.Equal (Color.BrightGreen, attribute.Background.GetClosestNamedColor16 ());
- }
-
- [Fact]
- [AutoInitShutdown]
- public void TestSerialize ()
- {
- // Test serializing to human-readable color names
- var attribute = new Attribute (Color.Blue, Color.Green);
- string json = JsonSerializer.Serialize (attribute, ConfigurationManagerTests._jsonOptions);
- Assert.Equal ("{\"Foreground\":\"Blue\",\"Background\":\"Green\"}", json);
- }
-}
-
-public class ColorSchemeJsonConverterTests
-{
- //string json = @"
- // {
- // ""ColorSchemes"": {
- // ""Base"": {
- // ""normal"": {
- // ""foreground"": ""White"",
- // ""background"": ""Blue""
- // },
- // ""focus"": {
- // ""foreground"": ""Black"",
- // ""background"": ""Gray""
- // },
- // ""hotNormal"": {
- // ""foreground"": ""BrightCyan"",
- // ""background"": ""Blue""
- // },
- // ""hotFocus"": {
- // ""foreground"": ""BrightBlue"",
- // ""background"": ""Gray""
- // },
- // ""disabled"": {
- // ""foreground"": ""DarkGray"",
- // ""background"": ""Blue""
- // }
- // }
- // }
- // }";
- [Fact]
- [AutoInitShutdown]
- public void TestColorSchemesSerialization ()
- {
- // Arrange
- var expectedColorScheme = new ColorScheme
- {
- Normal = new Attribute (Color.White, Color.Blue),
- Focus = new Attribute (Color.Black, Color.Gray),
- HotNormal = new Attribute (Color.BrightCyan, Color.Blue),
- HotFocus = new Attribute (Color.BrightBlue, Color.Gray),
- Disabled = new Attribute (Color.DarkGray, Color.Blue)
- };
-
- string serializedColorScheme =
- JsonSerializer.Serialize (expectedColorScheme, ConfigurationManagerTests._jsonOptions);
-
- // Act
- var actualColorScheme =
- JsonSerializer.Deserialize (serializedColorScheme, ConfigurationManagerTests._jsonOptions);
-
- // Assert
- Assert.Equal (expectedColorScheme, actualColorScheme);
- }
-}
-
-public class KeyCodeJsonConverterTests
-{
- [Theory]
- [InlineData (KeyCode.A, "A")]
- [InlineData (KeyCode.A | KeyCode.ShiftMask, "A, ShiftMask")]
- [InlineData (KeyCode.A | KeyCode.CtrlMask, "A, CtrlMask")]
- [InlineData (KeyCode.A | KeyCode.AltMask | KeyCode.CtrlMask, "A, CtrlMask, AltMask")]
- [InlineData ((KeyCode)'a' | KeyCode.AltMask | KeyCode.CtrlMask, "Space, A, CtrlMask, AltMask")]
- [InlineData ((KeyCode)'a' | KeyCode.ShiftMask, "Space, A, ShiftMask")]
- [InlineData (KeyCode.Delete | KeyCode.AltMask | KeyCode.CtrlMask, "Delete, CtrlMask, AltMask")]
- [InlineData (KeyCode.D4, "D4")]
- [InlineData (KeyCode.Esc, "Esc")]
- public void TestKeyRoundTripConversion (KeyCode key, string expectedStringTo)
- {
- // Arrange
- var options = new JsonSerializerOptions ();
- options.Converters.Add (new KeyCodeJsonConverter ());
-
- // Act
- string json = JsonSerializer.Serialize (key, options);
- var deserializedKey = JsonSerializer.Deserialize (json, options);
-
- // Assert
- Assert.Equal (expectedStringTo, deserializedKey.ToString ());
- }
-}
-
-public class KeyJsonConverterTests
-{
- [Theory]
- [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.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
- var options = new JsonSerializerOptions ();
- options.Converters.Add (new KeyJsonConverter ());
- options.Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping;
-
- // Act
- string json = JsonSerializer.Serialize ((Key)key, options);
-
- // Assert
- Assert.Equal (expected, json);
- }
-
- [Theory]
- [InlineData (KeyCode.A, "a")]
- [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.Delete | KeyCode.AltMask | KeyCode.CtrlMask, "Ctrl+Alt+Delete")]
- [InlineData (KeyCode.D4, "4")]
- [InlineData (KeyCode.Esc, "Esc")]
- public void TestKeyRoundTripConversion (KeyCode key, string expectedStringTo)
- {
- // Arrange
- var options = new JsonSerializerOptions ();
- options.Converters.Add (new KeyJsonConverter ());
- var encoderSettings = new TextEncoderSettings ();
- encoderSettings.AllowCharacters ('+', '-');
- encoderSettings.AllowRange (UnicodeRanges.BasicLatin);
- options.Encoder = JavaScriptEncoder.Create (encoderSettings);
-
- // Act
- string json = JsonSerializer.Serialize ((Key)key, options);
- var deserializedKey = JsonSerializer.Deserialize (json, options);
-
- // Assert
- Assert.Equal (expectedStringTo, deserializedKey.ToString ());
- }
-
- [Fact]
- public void Separator_Property_Serializes_As_Glyph ()
- {
- // Act
- string json = JsonSerializer.Serialize (Key.Separator, ConfigurationManager._serializerOptions);
-
- // Assert
- Assert.Equal ("\"+\"", json);
- }
-}
diff --git a/UnitTests/Configuration/KeyCodeJsonConverterTests.cs b/UnitTests/Configuration/KeyCodeJsonConverterTests.cs
new file mode 100644
index 000000000..0ee4fd877
--- /dev/null
+++ b/UnitTests/Configuration/KeyCodeJsonConverterTests.cs
@@ -0,0 +1,30 @@
+using System.Text.Json;
+
+namespace Terminal.Gui.ConfigurationTests;
+
+public class KeyCodeJsonConverterTests
+{
+ [Theory]
+ [InlineData (KeyCode.A, "A")]
+ [InlineData (KeyCode.A | KeyCode.ShiftMask, "A, ShiftMask")]
+ [InlineData (KeyCode.A | KeyCode.CtrlMask, "A, CtrlMask")]
+ [InlineData (KeyCode.A | KeyCode.AltMask | KeyCode.CtrlMask, "A, CtrlMask, AltMask")]
+ [InlineData ((KeyCode)'a' | KeyCode.AltMask | KeyCode.CtrlMask, "Space, A, CtrlMask, AltMask")]
+ [InlineData ((KeyCode)'a' | KeyCode.ShiftMask, "Space, A, ShiftMask")]
+ [InlineData (KeyCode.Delete | KeyCode.AltMask | KeyCode.CtrlMask, "Delete, CtrlMask, AltMask")]
+ [InlineData (KeyCode.D4, "D4")]
+ [InlineData (KeyCode.Esc, "Esc")]
+ public void TestKeyRoundTripConversion (KeyCode key, string expectedStringTo)
+ {
+ // Arrange
+ var options = new JsonSerializerOptions ();
+ options.Converters.Add (new KeyCodeJsonConverter ());
+
+ // Act
+ string json = JsonSerializer.Serialize (key, options);
+ var deserializedKey = JsonSerializer.Deserialize (json, options);
+
+ // Assert
+ Assert.Equal (expectedStringTo, deserializedKey.ToString ());
+ }
+}
\ No newline at end of file
diff --git a/UnitTests/Configuration/KeyJsonConverterTests.cs b/UnitTests/Configuration/KeyJsonConverterTests.cs
new file mode 100644
index 000000000..4e716c866
--- /dev/null
+++ b/UnitTests/Configuration/KeyJsonConverterTests.cs
@@ -0,0 +1,127 @@
+using System.Text;
+using System.Text.Encodings.Web;
+using System.Text.Json;
+using System.Text.Unicode;
+
+namespace Terminal.Gui.ConfigurationTests;
+
+public class KeyJsonConverterTests
+{
+ [Theory]
+ [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.Delete | KeyCode.AltMask | KeyCode.CtrlMask, "\"Ctrl+Alt+Delete\"")]
+ [InlineData (KeyCode.D4, "\"4\"")]
+ [InlineData (KeyCode.Esc, "\"Esc\"")]
+ [InlineData ((KeyCode)'+' | KeyCode.AltMask | KeyCode.CtrlMask, "\"Ctrl+Alt++\"")]
+ public void TestKey_Serialize (KeyCode key, string expected)
+ {
+ // Arrange
+ var options = new JsonSerializerOptions ();
+ options.Converters.Add (new KeyJsonConverter ());
+ options.Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping;
+
+ // Act
+ string json = JsonSerializer.Serialize ((Key)key, options);
+
+ // Assert
+ Assert.Equal (expected, json);
+ }
+
+ [Theory]
+ [InlineData (KeyCode.A, "a")]
+ [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.Delete | KeyCode.AltMask | KeyCode.CtrlMask, "Ctrl+Alt+Delete")]
+ [InlineData (KeyCode.D4, "4")]
+ [InlineData (KeyCode.Esc, "Esc")]
+ [InlineData ((KeyCode)'+' | KeyCode.AltMask | KeyCode.CtrlMask, "Ctrl+Alt++")]
+ [InlineData ((KeyCode)'+' | KeyCode.AltMask | KeyCode.CtrlMask, "Ctrl+Alt++")]
+ public void TestKeyRoundTripConversion (KeyCode key, string expectedStringTo)
+ {
+ // Arrange
+
+ // Act
+ string json = JsonSerializer.Serialize ((Key)key, ConfigurationManager._serializerOptions);
+ var deserializedKey = JsonSerializer.Deserialize (json, ConfigurationManager._serializerOptions);
+
+ // Assert
+ Assert.Equal (expectedStringTo, deserializedKey.ToString ());
+ }
+
+ [Fact]
+ public void Separator_Property_Serializes_As_Glyph ()
+ {
+ // Act
+ string json = JsonSerializer.Serialize (Key.Separator, ConfigurationManager._serializerOptions);
+
+ // Assert
+ Assert.Equal ($"\"{Key.Separator}\"", json);
+ }
+
+ [Fact]
+ public void Separator_Property_Set_Changes_Serialization_Format ()
+ {
+ Rune savedSeparator = Key.Separator;
+
+ try
+ {
+ // Act
+ Key.Separator = (Rune)'*';
+ string json = JsonSerializer.Serialize (Key.Separator, ConfigurationManager._serializerOptions);
+
+ // Assert
+ Assert.Equal ("\"*\"", json);
+ }
+ finally
+ {
+ Key.Separator = savedSeparator;
+ }
+
+ Key.Separator = savedSeparator;
+ }
+
+ [Theory]
+ [InlineData ('A', '+', "\"Ctrl+Alt+A\"")]
+ [InlineData ('A', '-', "\"Ctrl+Alt+A\"")]
+ [InlineData ('A', '*', "\"Ctrl+Alt+A\"")]
+ [InlineData ('A', '@', "\"Ctrl+Alt+A\"")]
+ [InlineData ('A', '+', "\"Ctrl@Alt@A\"")]
+ [InlineData ('A', '-', "\"Ctrl@Alt@A\"")]
+ [InlineData ('A', '*', "\"Ctrl@Alt@A\"")]
+ [InlineData ('A', '@', "\"Ctrl@Alt@A\"")]
+ [InlineData ('+', '+', "\"Ctrl+Alt++\"")]
+ [InlineData ('+', '-', "\"Ctrl+Alt++\"")]
+ [InlineData ('+', '*', "\"Ctrl+Alt++\"")]
+ [InlineData ('+', '@', "\"Ctrl+Alt++\"")]
+ [InlineData ('+', '+', "\"Ctrl@Alt@+\"")]
+ [InlineData ('+', '-', "\"Ctrl@Alt@+\"")]
+ [InlineData ('+', '*', "\"Ctrl@Alt@+\"")]
+ [InlineData ('+', '@', "\"Ctrl@Alt@+\"")]
+ public void Separator_Property_Set_Deserialization_Works_With_Any (char keyChar, char separator, string json)
+ {
+ Rune savedSeparator = Key.Separator;
+
+ try
+ {
+ // Act
+ Key.Separator = (Rune)separator;
+
+ Key deserializedKey = JsonSerializer.Deserialize (json, ConfigurationManager._serializerOptions);
+
+ Key expectedKey = new Key ((KeyCode)keyChar).WithCtrl.WithAlt;
+ // Assert
+ Assert.Equal (expectedKey, deserializedKey);
+ }
+ finally
+ {
+ Key.Separator = savedSeparator;
+ }
+
+ Key.Separator = savedSeparator;
+ }
+}
\ No newline at end of file
diff --git a/UnitTests/Input/KeyTests.cs b/UnitTests/Input/KeyTests.cs
index b3973b80e..fa2695e5f 100644
--- a/UnitTests/Input/KeyTests.cs
+++ b/UnitTests/Input/KeyTests.cs
@@ -17,9 +17,9 @@ public class KeyTests
{ "Alt+A", Key.A.WithAlt },
{ "Shift+A", Key.A.WithShift },
{ "A", Key.A.WithShift },
- { "â", new Key ((KeyCode)'â') },
- { "Shift+â", new Key ((KeyCode)'â' | KeyCode.ShiftMask) },
- { "Shift+Â", new Key ((KeyCode)'Â' | KeyCode.ShiftMask) },
+ { "â", new ((KeyCode)'â') },
+ { "Shift+â", new ((KeyCode)'â' | KeyCode.ShiftMask) },
+ { "Shift+Â", new ((KeyCode)'Â' | KeyCode.ShiftMask) },
{ "Ctrl+Shift+CursorUp", Key.CursorUp.WithShift.WithCtrl },
{ "Ctrl+Alt+Shift+CursorUp", Key.CursorUp.WithShift.WithCtrl.WithAlt },
{ "ctrl+alt+shift+cursorup", Key.CursorUp.WithShift.WithCtrl.WithAlt },
@@ -439,7 +439,7 @@ public class KeyTests
[Fact]
public void ToString_ShouldReturnReadableString ()
{
- var eventArgs = Key.A.WithCtrl;
+ Key eventArgs = Key.A.WithCtrl;
Assert.Equal ("Ctrl+A", eventArgs.ToString ());
}
@@ -454,8 +454,6 @@ public class KeyTests
[Theory]
[InlineData ("aa")]
[InlineData ("-1")]
- [InlineData ("Crtl-A")]
- [InlineData ("Ctrl=A")]
[InlineData ("Crtl")]
[InlineData ("99a")]
[InlineData ("a99")]
@@ -508,6 +506,8 @@ public class KeyTests
[InlineData ("Alt-A", KeyCode.A | KeyCode.AltMask)]
[InlineData ("A-Ctrl", KeyCode.A | KeyCode.CtrlMask)]
[InlineData ("Alt-A-Ctrl", KeyCode.A | KeyCode.CtrlMask | KeyCode.AltMask)]
+ [InlineData ("120", KeyCode.X)]
+ [InlineData ("Ctrl@A", KeyCode.A | KeyCode.CtrlMask)]
public void TryParse_ShouldReturnTrue_WhenValidKey (string keyString, KeyCode expected)
{
Assert.True (Key.TryParse (keyString, out Key key));
@@ -518,7 +518,7 @@ public class KeyTests
[Fact]
public void WithShift_ShouldReturnCorrectValue ()
{
- var a = Key.A;
+ Key a = Key.A;
Assert.Equal (KeyCode.A | KeyCode.ShiftMask, a.WithShift);
Key CAD = Key.Delete.WithCtrl.WithAlt;
@@ -529,17 +529,17 @@ public class KeyTests
[Fact]
public void Equals_ShouldReturnTrue_WhenEqual ()
{
- var a = Key.A;
- var b = Key.A;
+ Key a = Key.A;
+ Key b = Key.A;
Assert.True (a.Equals (b));
}
[Fact]
public void Equals_Handled_Changed_ShouldReturnTrue_WhenEqual ()
{
- var a = Key.A;
+ Key a = Key.A;
a.Handled = true;
- var b = Key.A;
+ Key b = Key.A;
b.Handled = true;
Assert.True (a.Equals (b));
}
@@ -547,9 +547,9 @@ public class KeyTests
[Fact]
public void Equals_Handled_Changed_ShouldReturnFalse_WhenNotEqual ()
{
- var a = Key.A;
+ Key a = Key.A;
a.Handled = true;
- var b = Key.A;
+ Key b = Key.A;
Assert.False (a.Equals (b));
}