Jelajahi Sumber

Fixes #3055: Remove `Key.Unknown` (#3057)

* Simplifies Key.IsValid

* Updated unit tests

* Fixed menu
Tig 1 tahun lalu
induk
melakukan
7f759b573b

+ 19 - 19
Terminal.Gui/Configuration/KeyCodeJsonConverter.cs

@@ -4,18 +4,19 @@ using System.Text.Json;
 using System.Text.Json.Serialization;
 
 namespace Terminal.Gui;
+
 class KeyCodeJsonConverter : JsonConverter<KeyCode> {
 	public override KeyCode Read (ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
 	{
 		if (reader.TokenType == JsonTokenType.StartObject) {
-			KeyCode key = KeyCode.Unknown;
-			Dictionary<string, KeyCode> modifierDict = new Dictionary<string, KeyCode> (comparer: StringComparer.InvariantCultureIgnoreCase) {
-					{ "Shift", KeyCode.ShiftMask },
-					{ "Ctrl", KeyCode.CtrlMask },
-					{ "Alt", KeyCode.AltMask }
-				};
+			KeyCode key = KeyCode.Null;
+			var modifierDict = new Dictionary<string, KeyCode> (comparer: StringComparer.InvariantCultureIgnoreCase) {
+				{ "Shift", KeyCode.ShiftMask },
+				{ "Ctrl", KeyCode.CtrlMask },
+				{ "Alt", KeyCode.AltMask }
+			};
 
-			List<KeyCode> modifiers = new List<KeyCode> ();
+			var modifiers = new List<KeyCode> ();
 
 			while (reader.Read ()) {
 				if (reader.TokenType == JsonTokenType.EndObject) {
@@ -38,7 +39,7 @@ class KeyCodeJsonConverter : JsonConverter<KeyCode> {
 								break;
 							}
 
-							if (key == KeyCode.Unknown || key == KeyCode.Null) {
+							if (key == KeyCode.Null) {
 								throw new JsonException ($"The value \"{reader.GetString ()}\" is not a valid Key.");
 							}
 
@@ -60,7 +61,7 @@ class KeyCodeJsonConverter : JsonConverter<KeyCode> {
 								if (reader.TokenType == JsonTokenType.EndArray) {
 									break;
 								}
-								var mod = reader.GetString ();
+								string mod = reader.GetString ();
 								try {
 									modifiers.Add (modifierDict [mod]);
 								} catch (KeyNotFoundException e) {
@@ -91,21 +92,20 @@ class KeyCodeJsonConverter : JsonConverter<KeyCode> {
 	{
 		writer.WriteStartObject ();
 
-		var keyName = (value & ~KeyCode.CtrlMask & ~KeyCode.ShiftMask & ~KeyCode.AltMask).ToString ();
+		string keyName = (value & ~KeyCode.CtrlMask & ~KeyCode.ShiftMask & ~KeyCode.AltMask).ToString ();
 		if (keyName != null) {
 			writer.WriteString ("Key", keyName);
 		} else {
 			writer.WriteNumber ("Key", (uint)(value & ~KeyCode.CtrlMask & ~KeyCode.ShiftMask & ~KeyCode.AltMask));
 		}
 
-		Dictionary<string, KeyCode> modifierDict = new Dictionary<string, KeyCode>
-		{
-				{ "Shift", KeyCode.ShiftMask },
-				{ "Ctrl", KeyCode.CtrlMask },
-				{ "Alt", KeyCode.AltMask }
-			    };
+		var modifierDict = new Dictionary<string, KeyCode> {
+			{ "Shift", KeyCode.ShiftMask },
+			{ "Ctrl", KeyCode.CtrlMask },
+			{ "Alt", KeyCode.AltMask }
+		};
 
-		List<string> modifiers = new List<string> ();
+		var modifiers = new List<string> ();
 		foreach (var pair in modifierDict) {
 			if ((value & pair.Value) == pair.Value) {
 				modifiers.Add (pair.Key);
@@ -115,7 +115,7 @@ class KeyCodeJsonConverter : JsonConverter<KeyCode> {
 		if (modifiers.Count > 0) {
 			writer.WritePropertyName ("Modifiers");
 			writer.WriteStartArray ();
-			foreach (var modifier in modifiers) {
+			foreach (string modifier in modifiers) {
 				writer.WriteStringValue (modifier);
 			}
 			writer.WriteEndArray ();
@@ -123,4 +123,4 @@ class KeyCodeJsonConverter : JsonConverter<KeyCode> {
 
 		writer.WriteEndObject ();
 	}
-}
+}

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

@@ -8,7 +8,7 @@ class KeyJsonConverter : JsonConverter<Key> {
 	public override Key Read (ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
 	{
 		if (reader.TokenType == JsonTokenType.StartObject) {
-			Key key = KeyCode.Unknown;
+			Key key = Key.Empty;
 			while (reader.Read ()) {
 				if (reader.TokenType == JsonTokenType.EndObject) {
 					break;

+ 0 - 5
Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs

@@ -1060,10 +1060,5 @@ public enum KeyCode : uint {
 	/// F24 key.
 	/// </summary>
 	F24,
-
-	/// <summary>
-	/// A key with an unknown mapping was raised.
-	/// </summary>
-	Unknown
 }
 

+ 0 - 3
Terminal.Gui/ConsoleDrivers/ConsoleKeyMapping.cs

@@ -263,9 +263,6 @@ namespace Terminal.Gui.ConsoleDrivers {
 				return ConsoleKey.F24;
 			case KeyCode.Tab | KeyCode.ShiftMask:
 				return ConsoleKey.Tab;
-			case KeyCode.Unknown:
-				isMappable = true;
-				return 0;
 			}
 
 			isMappable = true;

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

@@ -416,7 +416,7 @@ internal class CursesDriver : ConsoleDriver {
 		case Curses.AltCtrlKeyPPage: return KeyCode.PageUp | KeyCode.AltMask | KeyCode.CtrlMask;
 		case Curses.AltCtrlKeyHome: return KeyCode.Home | KeyCode.AltMask | KeyCode.CtrlMask;
 		case Curses.AltCtrlKeyEnd: return KeyCode.End | KeyCode.AltMask | KeyCode.CtrlMask;
-		default: return KeyCode.Unknown;
+		default: return KeyCode.Null;
 		}
 	}
 

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

@@ -253,7 +253,7 @@ public class FakeDriver : ConsoleDriver {
 		case ConsoleKey.OemPlus:
 		case ConsoleKey.OemMinus:
 			if (keyInfo.KeyChar == 0) {
-				return KeyCode.Unknown;
+				return KeyCode.Null;
 			}
 
 			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, (KeyCode)((uint)keyInfo.KeyChar));

+ 7 - 5
Terminal.Gui/Input/Key.cs

@@ -95,7 +95,7 @@ public class Key : EventArgs, IEquatable<Key> {
 	/// This property is the backing data for the <see cref="Key"/>. It is a <see cref="KeyCode"/> enum value.
 	/// </remarks>
 	[JsonInclude] [JsonConverter (typeof (KeyCodeJsonConverter))]
-	public KeyCode KeyCode { get; set; }
+	public KeyCode KeyCode { get; init; }
 
 	/// <summary>
 	/// Enables passing the key binding scope with the event. Default is <see cref="KeyBindingScope.Focused"/>.
@@ -195,9 +195,10 @@ public class Key : EventArgs, IEquatable<Key> {
 	}
 
 	/// <summary>
-	/// Indicates whether the <see cref="Key"/> is valid or not. 
+	/// Indicates whether the <see cref="Key"/> is valid or not. Invalid keys are <see cref="Key.Empty"/>,
+	/// and keys with only shift modifiers.
 	/// </summary>
-	public bool IsValid => KeyCode is not (KeyCode.Null or KeyCode.Unknown);
+	public bool IsValid => this != Empty && (NoAlt.NoShift.NoCtrl != Empty);
 
 	/// <summary>
 	/// Helper for specifying a shifted <see cref="Key"/>.
@@ -383,8 +384,9 @@ public class Key : EventArgs, IEquatable<Key> {
 	/// <returns>The formatted string. If the key is a printable character, it will be returned as a string. Otherwise, the key name will be returned.</returns>
 	public static string ToString (KeyCode key, Rune separator)
 	{
-		if (key is KeyCode.Null) {
-			return string.Empty;
+		if (key is KeyCode.Null || (key & ~KeyCode.CtrlMask & ~KeyCode.AltMask & ~KeyCode.ShiftMask) == 0) {
+			// Same as Key.IsValid
+			return @"Null";
 		}
 
 		var sb = new StringBuilder ();

+ 2 - 2
Terminal.Gui/Input/ShortcutHelper.cs

@@ -17,7 +17,7 @@ public class ShortcutHelper {
 	public virtual KeyCode Shortcut {
 		get => shortcut;
 		set {
-			if (shortcut != value && (PostShortcutValidation (value) || value is KeyCode.Null or KeyCode.Unknown)) {
+			if (shortcut != value && (PostShortcutValidation (value) || value is KeyCode.Null)) {
 				shortcut = value;
 			}
 		}
@@ -142,7 +142,7 @@ public class ShortcutHelper {
 		GetKeyToString (key, out KeyCode knm);
 
 		if (CheckKeysFlagRange (key, KeyCode.F1, KeyCode.F12) ||
-			((key & (KeyCode.CtrlMask | KeyCode.ShiftMask | KeyCode.AltMask)) != 0 && knm != KeyCode.Null && knm != KeyCode.Unknown)) {
+			((key & (KeyCode.CtrlMask | KeyCode.ShiftMask | KeyCode.AltMask)) != 0 && knm != KeyCode.Null)) {
 			return true;
 		}
 		Debug.WriteLine ($"WARNING: {Key.ToString (key)} is not a valid shortcut key.");

+ 1 - 1
Terminal.Gui/Text/TextFormatter.cs

@@ -984,7 +984,7 @@ namespace Terminal.Gui {
 				hotPos = hot_pos;
 
 				var newHotKey = (KeyCode)hot_key.Value;
-				if (newHotKey != KeyCode.Unknown && newHotKey != KeyCode.Null && !(newHotKey == KeyCode.Space || Rune.IsControl (hot_key))) {
+				if (newHotKey != KeyCode.Null && !(newHotKey == KeyCode.Space || Rune.IsControl (hot_key))) {
 					if ((newHotKey & ~KeyCode.Space) is >= KeyCode.A and <= KeyCode.Z) {
 						newHotKey &= ~KeyCode.Space;
 					}

+ 3 - 3
Terminal.Gui/View/ViewKeyboard.cs

@@ -76,7 +76,7 @@ public partial class View {
 	public virtual Key HotKey {
 		get => _hotKey;
 		set {
-			if (value is null || value.KeyCode == KeyCode.Unknown) {
+			if (value is null) {
 				throw new ArgumentException (@"HotKey must not be null. Use Key.Empty to clear the HotKey.", nameof (value));
 			}
 			if (AddKeyBindingsForHotKey (_hotKey, value)) {
@@ -115,7 +115,7 @@ public partial class View {
 			return false;
 		}
 
-		var newKey = hotKey == KeyCode.Unknown ? KeyCode.Null : hotKey;
+		var newKey = hotKey;
 
 		var baseKey = newKey.NoAlt.NoShift.NoCtrl;
 		if (newKey != Key.Empty && (baseKey == Key.Space || Rune.IsControl (baseKey.AsRune))) {
@@ -195,7 +195,7 @@ public partial class View {
 			return; // throw new InvalidOperationException ("Can't set HotKey unless a TextFormatter has been created");
 		}
 		if (TextFormatter.FindHotKey (_text, HotKeySpecifier, true, out _, out var hk)) {
-			if (_hotKey.KeyCode != hk && hk != KeyCode.Unknown) {
+			if (_hotKey.KeyCode != hk) {
 				HotKey = hk;
 			}
 		} else {

+ 7 - 8
Terminal.Gui/Views/CheckBox.cs

@@ -107,24 +107,23 @@ public class CheckBox : View {
 	public override Key HotKey {
 		get => base.HotKey;
 		set {
-			if (value is null || value.KeyCode is KeyCode.Unknown) {
+			if (value is null) {
 				throw new ArgumentException (nameof (value));
 			}
 
 			var prev = base.HotKey;
 			if (prev != value) {
-				var v = value == KeyCode.Unknown ? Key.Empty: value;
-				base.HotKey = TextFormatter.HotKey = v;
+				base.HotKey = TextFormatter.HotKey = value;
 
 				// Also add Alt+HotKey
-				if (prev != (Key)KeyCode.Null && KeyBindings.TryGet (prev.WithAlt, out _)) {
-					if (v.KeyCode == KeyCode.Null) {
+				if (prev != Key.Empty && KeyBindings.TryGet (prev.WithAlt, out _)) {
+					if (value.KeyCode == KeyCode.Null) {
 						KeyBindings.Remove (prev.WithAlt);
 					} else {
-						KeyBindings.Replace (prev.WithAlt, v.WithAlt);
+						KeyBindings.Replace (prev.WithAlt, value.WithAlt);
 					}
-				} else if (v.KeyCode != KeyCode.Null) {
-					KeyBindings.Add (v.WithAlt, Command.Accept);
+				} else if (value != Key.Empty) {
+					KeyBindings.Add (value.WithAlt, Command.Accept);
 				}
 			}
 		}

+ 14 - 14
Terminal.Gui/Views/Menu/Menu.cs

@@ -133,7 +133,7 @@ public class MenuItem {
 	/// <summary>
 	/// Gets the text describing the keystroke combination defined by <see cref="Shortcut"/>.
 	/// </summary>
-	public string ShortcutTag => Key.ToString (_shortcutHelper.Shortcut, MenuBar.ShortcutDelimiter);
+	public string ShortcutTag => _shortcutHelper.Shortcut == KeyCode.Null ? string.Empty : Key.ToString (_shortcutHelper.Shortcut, MenuBar.ShortcutDelimiter);
 	#endregion Keyboard Handling
 
 	/// <summary>
@@ -264,15 +264,15 @@ public class MenuItem {
 		bool? previousChecked = Checked;
 		if (AllowNullChecked) {
 			switch (previousChecked) {
-				case null:
-					Checked = true;
-					break;
-				case true:
-					Checked = false;
-					break;
-				case false:
-					Checked = null;
-					break;
+			case null:
+				Checked = true;
+				break;
+			case true:
+				Checked = false;
+				break;
+			case false:
+				Checked = null;
+				break;
 			}
 		} else {
 			Checked = !Checked;
@@ -331,8 +331,8 @@ class Menu : View {
 
 		if (barItems == null) {
 			throw new ArgumentNullException (nameof (barItems));
-		} 
-		
+		}
+
 		_host = host;
 		_barItems = barItems;
 
@@ -432,7 +432,7 @@ class Menu : View {
 		foreach (var menuItem in menuBarItem.Children.Where (m => m != null)) {
 			KeyBindings.Add ((KeyCode)menuItem.HotKey.Value, Command.ToggleExpandCollapse);
 			KeyBindings.Add ((KeyCode)menuItem.HotKey.Value | KeyCode.AltMask, Command.ToggleExpandCollapse);
-			if (menuItem.Shortcut != KeyCode.Unknown) {
+			if (menuItem.Shortcut != KeyCode.Null) {
 				KeyBindings.Add (menuItem.Shortcut, KeyBindingScope.HotKey, Command.Select);
 			}
 			var subMenu = menuBarItem.SubMenu (menuItem);
@@ -494,7 +494,7 @@ class Menu : View {
 
 		var key = keyEvent.KeyCode;
 
-		if (KeyBindings.TryGet(key, out _)) {
+		if (KeyBindings.TryGet (key, out _)) {
 			_menuBarItemToActivate = -1;
 			_menuItemToSelect = null;
 

+ 2 - 2
Terminal.Gui/Views/Menu/MenuBar.cs

@@ -162,7 +162,7 @@ public class MenuBarItem : MenuItem {
 				menuBar.KeyBindings.Add ((KeyCode)menuItem.HotKey.Value, Command.ToggleExpandCollapse);
 				menuBar.KeyBindings.Add ((KeyCode)menuItem.HotKey.Value | KeyCode.AltMask, KeyBindingScope.HotKey, Command.ToggleExpandCollapse);
 			}
-			if (menuItem.Shortcut != KeyCode.Unknown && menuItem.Shortcut != KeyCode.Null) {
+			if (menuItem.Shortcut != KeyCode.Null) {
 				menuBar.KeyBindings.Add (menuItem.Shortcut, KeyBindingScope.HotKey, Command.Select);
 			}
 			SubMenu (menuItem)?.AddKeyBindings (menuBar);
@@ -323,7 +323,7 @@ public class MenuBar : View {
 					KeyBindings.Add ((KeyCode)menuBarItem.HotKey.Value, Command.ToggleExpandCollapse);
 					KeyBindings.Add ((KeyCode)menuBarItem.HotKey.Value | KeyCode.AltMask, KeyBindingScope.HotKey, Command.ToggleExpandCollapse);
 				}
-				if (menuBarItem.Shortcut != KeyCode.Unknown && menuBarItem.Shortcut != KeyCode.Null) {
+				if (menuBarItem.Shortcut != KeyCode.Null) {
 					// Technically this will will never run because MenuBarItems don't have shortcuts
 					KeyBindings.Add (menuBarItem.Shortcut, KeyBindingScope.HotKey, Command.Select);
 				}

+ 1 - 1
Terminal.Gui/Views/RadioGroup.cs

@@ -244,7 +244,7 @@ public class RadioGroup : View {
 			Driver.SetAttribute (GetNormalColor ());
 			Driver.AddStr ($"{(i == _selected ? CM.Glyphs.Selected : CM.Glyphs.UnSelected)} ");
 			TextFormatter.FindHotKey (rl, HotKeySpecifier, true, out int hotPos, out var hotKey);
-			if (hotPos != -1 && (hotKey != KeyCode.Null || hotKey != KeyCode.Unknown)) {
+			if (hotPos != -1 && (hotKey != KeyCode.Null)) {
 				var rlRunes = rl.ToRunes ();
 				for (int j = 0; j < rlRunes.Length; j++) {
 					Rune rune = rlRunes [j];

+ 102 - 102
UICatalog/Scenarios/Unicode.cs

@@ -2,119 +2,119 @@
 using System.Text;
 using Terminal.Gui;
 
-namespace UICatalog.Scenarios {
-	[ScenarioMetadata (Name: "Unicode", Description: "Tries to test Unicode in all controls (#204)")]
-	[ScenarioCategory ("Text and Formatting")]
-	[ScenarioCategory ("Controls")]
-	public class UnicodeInMenu : Scenario {
-		public override void Setup ()
-		{
-			string unicode = "Τὴ γλῶσσα μοῦ ἔδωσαν ἑλληνικὴ\nτὸ σπίτι φτωχικὸ στὶς ἀμμουδιὲς τοῦ Ὁμήρου.\nΜονάχη ἔγνοια ἡ γλῶσσα μου στὶς ἀμμουδιὲς τοῦ Ὁμήρου.";
+namespace UICatalog.Scenarios; 
 
-			string gitString = $"gui.cs 糊 (hú) {ConfigurationManager.Glyphs.IdenticalTo} {ConfigurationManager.Glyphs.DownArrow}18 {ConfigurationManager.Glyphs.UpArrow}10 {ConfigurationManager.Glyphs.VerticalFourDots}1 {ConfigurationManager.Glyphs.HorizontalEllipsis}";
+[ScenarioMetadata ("Unicode", "Tries to test Unicode in all controls (#204)")]
+[ScenarioCategory ("Text and Formatting")]
+[ScenarioCategory ("Controls")]
+public class UnicodeInMenu : Scenario {
+	public override void Setup ()
+	{
+		string unicode = "Τὴ γλῶσσα μοῦ ἔδωσαν ἑλληνικὴ\nτὸ σπίτι φτωχικὸ στὶς ἀμμουδιὲς τοῦ Ὁμήρου.\nΜονάχη ἔγνοια ἡ γλῶσσα μου στὶς ἀμμουδιὲς τοῦ Ὁμήρου.";
 
-			var menu = new MenuBar (new MenuBarItem [] {
-				new MenuBarItem ("_Файл", new MenuItem [] {
-					new MenuItem ("_Создать", "Creates new file", null),
-					new MenuItem ("_Открыть", "", null),
-					new MenuItem ("Со_хранить", "", null),
-					new MenuItem ("_Выход", "", () => Application.RequestStop() )
-				}),
-				new MenuBarItem ("_Edit", new MenuItem [] {
-					new MenuItem ("_Copy", "", null),
-					new MenuItem ("C_ut", "", null),
-					new MenuItem ("_糊", "hú (Paste)", null)
-				})
-			});
-			Application.Top.Add (menu);
+		string gitString = $"gui.cs 糊 (hú) {ConfigurationManager.Glyphs.IdenticalTo} {ConfigurationManager.Glyphs.DownArrow}18 {ConfigurationManager.Glyphs.UpArrow}10 {ConfigurationManager.Glyphs.VerticalFourDots}1 {ConfigurationManager.Glyphs.HorizontalEllipsis}";
 
-			var statusBar = new StatusBar (new StatusItem [] {
-				new StatusItem(Application.QuitKey, $"{Application.QuitKey} Выход", () => Application.RequestStop()),
-				new StatusItem (KeyCode.Unknown, "~F2~ Создать", null),
-				new StatusItem(KeyCode.Unknown, "~F3~ Со_хранить", null),
-			});
-			Application.Top.Add (statusBar);
+		var menu = new MenuBar (new MenuBarItem [] {
+			new ("_Файл", new MenuItem [] {
+				new ("_Создать", "Creates new file", null),
+				new ("_Открыть", "", null),
+				new ("Со_хранить", "", null),
+				new ("_Выход", "", () => Application.RequestStop ())
+			}),
+			new ("_Edit", new MenuItem [] {
+				new ("_Copy", "", null),
+				new ("C_ut", "", null),
+				new ("_糊", "hú (Paste)", null)
+			})
+		});
+		Application.Top.Add (menu);
 
-			var label = new Label ("Label:") { X = 0, Y = 1 };
-			Win.Add (label);
-			var testlabel = new Label (gitString) { X = 20, Y = Pos.Y (label), Width = Dim.Percent (50), };
-			Win.Add (testlabel);
+		var statusBar = new StatusBar (new StatusItem [] {
+			new (Application.QuitKey, $"{Application.QuitKey} Выход", () => Application.RequestStop ()),
+			new (KeyCode.Null, "~F2~ Создать", null),
+			new (KeyCode.Null, "~F3~ Со_хранить", null)
+		});
+		Application.Top.Add (statusBar);
 
-			label = new Label ("Label (CanFocus):") { X = Pos.X (label), Y = Pos.Bottom (label) + 1 };
-			Win.Add (label);
-			var sb = new StringBuilder ();
-			sb.Append ('e');
-			sb.Append ('\u0301');
-			sb.Append ('\u0301');
-			testlabel = new Label ($"Should be [e with two accents, but isn't due to #2616]: [{sb}]") { X = 20, Y = Pos.Y (label), Width = Dim.Percent (50), CanFocus = true, HotKeySpecifier = new Rune ('&') };
-			Win.Add (testlabel);
-			label = new Label ("Button:") { X = Pos.X (label), Y = Pos.Bottom (label) + 1 };
-			Win.Add (label);
-			var button = new Button ("A123456789♥♦♣♠JQK") { X = 20, Y = Pos.Y (label) };
-			Win.Add (button);
+		var label = new Label ("Label:") { X = 0, Y = 1 };
+		Win.Add (label);
+		var testlabel = new Label (gitString) { X = 20, Y = Pos.Y (label), Width = Dim.Percent (50) };
+		Win.Add (testlabel);
 
-			label = new Label ("CheckBox:") { X = Pos.X (label), Y = Pos.Bottom (label) + 1 };
-			Win.Add (label);
-			var checkBox = new CheckBox (gitString) { X = 20, Y = Pos.Y (label), Width = Dim.Percent (50) };
-			var checkBoxRight = new CheckBox ($"Align Right - {gitString}") { X = 20, Y = Pos.Bottom (checkBox), Width = Dim.Percent (50), TextAlignment = TextAlignment.Right};
-			Win.Add (checkBox, checkBoxRight);
+		label = new Label ("Label (CanFocus):") { X = Pos.X (label), Y = Pos.Bottom (label) + 1 };
+		Win.Add (label);
+		var sb = new StringBuilder ();
+		sb.Append ('e');
+		sb.Append ('\u0301');
+		sb.Append ('\u0301');
+		testlabel = new Label ($"Should be [e with two accents, but isn't due to #2616]: [{sb}]") { X = 20, Y = Pos.Y (label), Width = Dim.Percent (50), CanFocus = true, HotKeySpecifier = new Rune ('&') };
+		Win.Add (testlabel);
+		label = new Label ("Button:") { X = Pos.X (label), Y = Pos.Bottom (label) + 1 };
+		Win.Add (label);
+		var button = new Button ("A123456789♥♦♣♠JQK") { X = 20, Y = Pos.Y (label) };
+		Win.Add (button);
 
-			label = new Label ("ComboBox:") { X = Pos.X (label), Y = Pos.Bottom (checkBoxRight) + 1 };
-			Win.Add (label);
-			var comboBox = new ComboBox () {
-				X = 20,
-				Y = Pos.Y (label),
-				Width = Dim.Percent (50)
-			};
-			comboBox.SetSource (new List<string> () { gitString, "Со_хранить" });
+		label = new Label ("CheckBox:") { X = Pos.X (label), Y = Pos.Bottom (label) + 1 };
+		Win.Add (label);
+		var checkBox = new CheckBox (gitString) { X = 20, Y = Pos.Y (label), Width = Dim.Percent (50) };
+		var checkBoxRight = new CheckBox ($"Align Right - {gitString}") { X = 20, Y = Pos.Bottom (checkBox), Width = Dim.Percent (50), TextAlignment = TextAlignment.Right };
+		Win.Add (checkBox, checkBoxRight);
 
-			Win.Add (comboBox);
-			comboBox.Text = gitString;
+		label = new Label ("ComboBox:") { X = Pos.X (label), Y = Pos.Bottom (checkBoxRight) + 1 };
+		Win.Add (label);
+		var comboBox = new ComboBox () {
+			X = 20,
+			Y = Pos.Y (label),
+			Width = Dim.Percent (50)
+		};
+		comboBox.SetSource (new List<string> () { gitString, "Со_хранить" });
 
-			label = new Label ("HexView:") { X = Pos.X (label), Y = Pos.Bottom (label) + 2 };
-			Win.Add (label);
-			var hexView = new HexView (new System.IO.MemoryStream (Encoding.ASCII.GetBytes (gitString + " Со_хранить"))) {
-				X = 20,
-				Y = Pos.Y (label),
-				Width = Dim.Percent (60),
-				Height = 5
-			};
-			Win.Add (hexView);
+		Win.Add (comboBox);
+		comboBox.Text = gitString;
 
-			label = new Label ("ListView:") { X = Pos.X (label), Y = Pos.Bottom (hexView) + 1 };
-			Win.Add (label);
-			var listView = new ListView (new List<string> () { "item #1", gitString, "Со_хранить", unicode }) {
-				X = 20,
-				Y = Pos.Y (label),
-				Width = Dim.Percent (60),
-				Height = 3,
-			};
-			Win.Add (listView);
+		label = new Label ("HexView:") { X = Pos.X (label), Y = Pos.Bottom (label) + 2 };
+		Win.Add (label);
+		var hexView = new HexView (new System.IO.MemoryStream (Encoding.ASCII.GetBytes (gitString + " Со_хранить"))) {
+			X = 20,
+			Y = Pos.Y (label),
+			Width = Dim.Percent (60),
+			Height = 5
+		};
+		Win.Add (hexView);
 
-			label = new Label ("RadioGroup:") { X = Pos.X (label), Y = Pos.Bottom (listView) + 1 };
-			Win.Add (label);
-			var radioGroup = new RadioGroup (new string [] { "item #1", gitString, "Со_хранить", "𝔽𝕆𝕆𝔹𝔸ℝ" }, selected: 0) {
-				X = 20,
-				Y = Pos.Y (label),
-				Width = Dim.Percent (60),
-			};
-			Win.Add (radioGroup);
+		label = new Label ("ListView:") { X = Pos.X (label), Y = Pos.Bottom (hexView) + 1 };
+		Win.Add (label);
+		var listView = new ListView (new List<string> () { "item #1", gitString, "Со_хранить", unicode }) {
+			X = 20,
+			Y = Pos.Y (label),
+			Width = Dim.Percent (60),
+			Height = 3
+		};
+		Win.Add (listView);
 
-			label = new Label ("TextField:") { X = Pos.X (label), Y = Pos.Bottom (radioGroup) + 1 };
-			Win.Add (label);
-			var textField = new TextField (gitString + " = Со_хранить") { X = 20, Y = Pos.Y (label), Width = Dim.Percent (60) };
-			Win.Add (textField);
+		label = new Label ("RadioGroup:") { X = Pos.X (label), Y = Pos.Bottom (listView) + 1 };
+		Win.Add (label);
+		var radioGroup = new RadioGroup (new string [] { "item #1", gitString, "Со_хранить", "𝔽𝕆𝕆𝔹𝔸ℝ" }, 0) {
+			X = 20,
+			Y = Pos.Y (label),
+			Width = Dim.Percent (60)
+		};
+		Win.Add (radioGroup);
 
-			label = new Label ("TextView:") { X = Pos.X (label), Y = Pos.Bottom (textField) + 1 };
-			Win.Add (label);
-			var textView = new TextView () {
-				X = 20,
-				Y = Pos.Y (label),
-				Width = Dim.Percent (60),
-				Height = 5,
-				Text = unicode,
-			};
-			Win.Add (textView);
-		}
+		label = new Label ("TextField:") { X = Pos.X (label), Y = Pos.Bottom (radioGroup) + 1 };
+		Win.Add (label);
+		var textField = new TextField (gitString + " = Со_хранить") { X = 20, Y = Pos.Y (label), Width = Dim.Percent (60) };
+		Win.Add (textField);
+
+		label = new Label ("TextView:") { X = Pos.X (label), Y = Pos.Bottom (textField) + 1 };
+		Win.Add (label);
+		var textView = new TextView () {
+			X = 20,
+			Y = Pos.Y (label),
+			Width = Dim.Percent (60),
+			Height = 5,
+			Text = unicode
+		};
+		Win.Add (textView);
 	}
-}
+}

+ 208 - 208
UICatalog/Scenarios/VkeyPacketSimulator.cs

@@ -5,221 +5,221 @@ using System.Threading.Tasks;
 using Terminal.Gui;
 using Terminal.Gui.ConsoleDrivers;
 
-namespace UICatalog.Scenarios {
-	[ScenarioMetadata (Name: "VkeyPacketSimulator", Description: "Simulates the Virtual Key Packet")]
-	[ScenarioCategory ("Mouse and Keyboard")]
-	public class VkeyPacketSimulator : Scenario {
-		List<int> _keyboardStrokes = new List<int> ();
-		bool _outputStarted = false;
-		bool _wasUnknown = false;
-		static ManualResetEventSlim _stopOutput = new ManualResetEventSlim (false);
-
-		public override void Setup ()
-		{
-			var label = new Label ("Input") {
-				X = Pos.Center ()
-			};
-			Win.Add (label);
-
-			var btnInput = new Button ("Select Input") {
-				X = Pos.AnchorEnd (16),
-			};
-			Win.Add (btnInput);
-
-			const string ruler = "|123456789";
-
-			var inputHorizontalRuler = new Label ("") {
-				Y = Pos.Bottom (btnInput),
-				Width = Dim.Fill (),
-				ColorScheme = Colors.Error,
-				AutoSize = false
-			};
-			Win.Add (inputHorizontalRuler);
-
-			var inputVerticalRuler = new Label ("", TextDirection.TopBottom_LeftRight) {
-				Y = Pos.Bottom (btnInput),
-				Width = 1,
-				ColorScheme = Colors.Error,
-				AutoSize = false
-			};
-			Win.Add (inputVerticalRuler);
-
-			var tvInput = new TextView {
-				Title = "Input",
-				X = 1,
-				Y = Pos.Bottom (inputHorizontalRuler),
-				Width = Dim.Fill (),
-				Height = Dim.Percent (50) - 1
-			};
-			Win.Add (tvInput);
-
-			label = new Label ("Output") {
-				X = Pos.Center (),
-				Y = Pos.Bottom (tvInput)
-			};
-			Win.Add (label);
-
-			var btnOutput = new Button ("Select Output") {
-				X = Pos.AnchorEnd (17),
-				Y = Pos.Top (label)
-			};
-			Win.Add (btnOutput);
-
-			var outputHorizontalRuler = new Label ("") {
-				Y = Pos.Bottom (btnOutput),
-				Width = Dim.Fill (),
-				ColorScheme = Colors.Error,
-				AutoSize = false
-			};
-			Win.Add (outputHorizontalRuler);
-
-			var outputVerticalRuler = new Label ("", TextDirection.TopBottom_LeftRight) {
-				Y = Pos.Bottom (btnOutput),
-				Width = 1,
-				Height = Dim.Fill (),
-				ColorScheme = Colors.Error,
-				AutoSize = false
-			};
-			Win.Add (outputVerticalRuler);
-
-			var tvOutput = new TextView {
-				Title = "Output",
-				X = 1,
-				Y = Pos.Bottom (outputHorizontalRuler),
-				Width = Dim.Fill (),
-				Height = Dim.Fill (),
-				ReadOnly = true
-			};
-
-			// Detect unknown keys and reject them.
-			tvOutput.KeyDown += (s, e) => {
-				//System.Diagnostics.Debug.WriteLine ($"Output - KeyDown: {e.Key}");
-				//e.Handled = true;
-				if (e.KeyCode == KeyCode.Unknown) {
-					_wasUnknown = true;
-				}
-			};
-
-			tvOutput.KeyUp += (s, e) => {
-				//System.Diagnostics.Debug.WriteLine ($"Output - KeyPress - _keyboardStrokes: {_keyboardStrokes.Count}");
-				if (_outputStarted && _keyboardStrokes.Count > 0) {
-					//// TODO: Tig: I don't understand what this is trying to do
-					//if (!tvOutput.ProcessKeyDown (e)) {
-					//	Application.Invoke (() => {
-					//		MessageBox.Query ("Keys", $"'{KeyEventArgs.ToString (e.ConsoleDriverKey, MenuBar.ShortcutDelimiter)}' pressed!", "Ok");
-					//	});
-					//}
-					e.Handled = true;
-					_stopOutput.Set ();
-				}
-				//System.Diagnostics.Debug.WriteLine ($"Output - KeyPress - _keyboardStrokes: {_keyboardStrokes.Count}");
-			};
-
-			Win.Add (tvOutput);
-
-			Key unknownChar = null;
-
-			tvInput.KeyUp += (s, e) => {
-				//System.Diagnostics.Debug.WriteLine ($"Input - KeyUp: {e.Key}");
-				//var ke = e;
-
-				if (e.KeyCode == KeyCode.Unknown) {
-					_wasUnknown = true;
-					e.Handled = true;
-					return;
-				}
-				if (_wasUnknown && _keyboardStrokes.Count == 1) {
-					_wasUnknown = false;
-				} else if (_wasUnknown && char.IsLetter ((char)e.KeyCode)) {
-					_wasUnknown = false;
-				}
-				if (_keyboardStrokes.Count == 0) {
-					AddKeyboardStrokes (e);
-				} else {
-					_keyboardStrokes.Insert (0, 0);
-				}
-				if (_wasUnknown && (int)e.KeyCode - (int)(e.KeyCode & (KeyCode.AltMask | KeyCode.CtrlMask | KeyCode.ShiftMask)) != 0) {
-					unknownChar = e;
-				}
+namespace UICatalog.Scenarios; 
+
+[ScenarioMetadata ("VkeyPacketSimulator", "Simulates the Virtual Key Packet")]
+[ScenarioCategory ("Mouse and Keyboard")]
+public class VkeyPacketSimulator : Scenario {
+	List<int> _keyboardStrokes = new ();
+	bool _outputStarted = false;
+	bool _wasUnknown = false;
+	static ManualResetEventSlim _stopOutput = new (false);
+
+	public override void Setup ()
+	{
+		var label = new Label ("Input") {
+			X = Pos.Center ()
+		};
+		Win.Add (label);
+
+		var btnInput = new Button ("Select Input") {
+			X = Pos.AnchorEnd (16)
+		};
+		Win.Add (btnInput);
+
+		const string ruler = "|123456789";
+
+		var inputHorizontalRuler = new Label ("") {
+			Y = Pos.Bottom (btnInput),
+			Width = Dim.Fill (),
+			ColorScheme = Colors.Error,
+			AutoSize = false
+		};
+		Win.Add (inputHorizontalRuler);
+
+		var inputVerticalRuler = new Label ("", TextDirection.TopBottom_LeftRight) {
+			Y = Pos.Bottom (btnInput),
+			Width = 1,
+			ColorScheme = Colors.Error,
+			AutoSize = false
+		};
+		Win.Add (inputVerticalRuler);
+
+		var tvInput = new TextView {
+			Title = "Input",
+			X = 1,
+			Y = Pos.Bottom (inputHorizontalRuler),
+			Width = Dim.Fill (),
+			Height = Dim.Percent (50) - 1
+		};
+		Win.Add (tvInput);
+
+		label = new Label ("Output") {
+			X = Pos.Center (),
+			Y = Pos.Bottom (tvInput)
+		};
+		Win.Add (label);
+
+		var btnOutput = new Button ("Select Output") {
+			X = Pos.AnchorEnd (17),
+			Y = Pos.Top (label)
+		};
+		Win.Add (btnOutput);
+
+		var outputHorizontalRuler = new Label ("") {
+			Y = Pos.Bottom (btnOutput),
+			Width = Dim.Fill (),
+			ColorScheme = Colors.Error,
+			AutoSize = false
+		};
+		Win.Add (outputHorizontalRuler);
+
+		var outputVerticalRuler = new Label ("", TextDirection.TopBottom_LeftRight) {
+			Y = Pos.Bottom (btnOutput),
+			Width = 1,
+			Height = Dim.Fill (),
+			ColorScheme = Colors.Error,
+			AutoSize = false
+		};
+		Win.Add (outputVerticalRuler);
+
+		var tvOutput = new TextView {
+			Title = "Output",
+			X = 1,
+			Y = Pos.Bottom (outputHorizontalRuler),
+			Width = Dim.Fill (),
+			Height = Dim.Fill (),
+			ReadOnly = true
+		};
+
+		// Detect unknown keys and reject them.
+		tvOutput.KeyDown += (s, e) => {
+			//System.Diagnostics.Debug.WriteLine ($"Output - KeyDown: {e.Key}");
+			//e.Handled = true;
+			if (e == Key.Empty) {
+				_wasUnknown = true;
+			}
+		};
+
+		tvOutput.KeyUp += (s, e) => {
+			//System.Diagnostics.Debug.WriteLine ($"Output - KeyPress - _keyboardStrokes: {_keyboardStrokes.Count}");
+			if (_outputStarted && _keyboardStrokes.Count > 0) {
+				//// TODO: Tig: I don't understand what this is trying to do
+				//if (!tvOutput.ProcessKeyDown (e)) {
+				//	Application.Invoke (() => {
+				//		MessageBox.Query ("Keys", $"'{KeyEventArgs.ToString (e.ConsoleDriverKey, MenuBar.ShortcutDelimiter)}' pressed!", "Ok");
+				//	});
+				//}
+				e.Handled = true;
+				_stopOutput.Set ();
+			}
+			//System.Diagnostics.Debug.WriteLine ($"Output - KeyPress - _keyboardStrokes: {_keyboardStrokes.Count}");
+		};
+
+		Win.Add (tvOutput);
+
+		Key unknownChar = null;
+
+		tvInput.KeyUp += (s, e) => {
+			//System.Diagnostics.Debug.WriteLine ($"Input - KeyUp: {e.Key}");
+			//var ke = e;
+
+			if (e == Key.Empty) {
+				_wasUnknown = true;
 				e.Handled = true;
-				if (!_wasUnknown && _keyboardStrokes.Count > 0) {
-					_outputStarted = true;
-					tvOutput.ReadOnly = false;
-					tvOutput.SetFocus ();
-					tvOutput.SetNeedsDisplay ();
-
-					Task.Run (() => {
-						while (_outputStarted) {
-							try {
-								ConsoleModifiers mod = new ConsoleModifiers ();
-								if (e.KeyCode.HasFlag (KeyCode.ShiftMask)) {
-									mod |= ConsoleModifiers.Shift;
-								}
-								if (e.KeyCode.HasFlag (KeyCode.AltMask)) {
-									mod |= ConsoleModifiers.Alt;
-								}
-								if (e.KeyCode.HasFlag (KeyCode.CtrlMask)) {
-									mod |= ConsoleModifiers.Control;
-								}
-								for (int i = 0; i < _keyboardStrokes.Count; i++) {
-									var consoleKeyInfo = ConsoleKeyMapping.GetConsoleKeyFromKey ((uint)_keyboardStrokes [i], mod, out _);
-									Application.Driver.SendKeys (consoleKeyInfo.KeyChar, ConsoleKey.Packet, mod.HasFlag (ConsoleModifiers.Shift),
-										mod.HasFlag (ConsoleModifiers.Alt), mod.HasFlag (ConsoleModifiers.Control));
-								}
-								//}
-							} catch (Exception) {
-								Application.Invoke (() => {
-									MessageBox.ErrorQuery ("Error", "Couldn't send the keystrokes!", "Ok");
-									Application.RequestStop ();
-								});
+				return;
+			}
+			if (_wasUnknown && _keyboardStrokes.Count == 1) {
+				_wasUnknown = false;
+			} else if (_wasUnknown && char.IsLetter ((char)e.KeyCode)) {
+				_wasUnknown = false;
+			}
+			if (_keyboardStrokes.Count == 0) {
+				AddKeyboardStrokes (e);
+			} else {
+				_keyboardStrokes.Insert (0, 0);
+			}
+			if (_wasUnknown && (int)e.KeyCode - (int)(e.KeyCode & (KeyCode.AltMask | KeyCode.CtrlMask | KeyCode.ShiftMask)) != 0) {
+				unknownChar = e;
+			}
+			e.Handled = true;
+			if (!_wasUnknown && _keyboardStrokes.Count > 0) {
+				_outputStarted = true;
+				tvOutput.ReadOnly = false;
+				tvOutput.SetFocus ();
+				tvOutput.SetNeedsDisplay ();
+
+				Task.Run (() => {
+					while (_outputStarted) {
+						try {
+							var mod = new ConsoleModifiers ();
+							if (e.KeyCode.HasFlag (KeyCode.ShiftMask)) {
+								mod |= ConsoleModifiers.Shift;
 							}
-							_stopOutput.Wait ();
-							_stopOutput.Reset ();
-							_keyboardStrokes.RemoveAt (0);
-							if (_keyboardStrokes.Count == 0) {
-								_outputStarted = false;
-								Application.Invoke (() => {
-									tvOutput.ReadOnly = true;
-									tvInput.SetFocus ();
-								});
+							if (e.KeyCode.HasFlag (KeyCode.AltMask)) {
+								mod |= ConsoleModifiers.Alt;
 							}
+							if (e.KeyCode.HasFlag (KeyCode.CtrlMask)) {
+								mod |= ConsoleModifiers.Control;
+							}
+							for (int i = 0; i < _keyboardStrokes.Count; i++) {
+								var consoleKeyInfo = ConsoleKeyMapping.GetConsoleKeyFromKey ((uint)_keyboardStrokes [i], mod, out _);
+								Application.Driver.SendKeys (consoleKeyInfo.KeyChar, ConsoleKey.Packet, mod.HasFlag (ConsoleModifiers.Shift),
+									mod.HasFlag (ConsoleModifiers.Alt), mod.HasFlag (ConsoleModifiers.Control));
+							}
+							//}
+						} catch (Exception) {
+							Application.Invoke (() => {
+								MessageBox.ErrorQuery ("Error", "Couldn't send the keystrokes!", "Ok");
+								Application.RequestStop ();
+							});
+						}
+						_stopOutput.Wait ();
+						_stopOutput.Reset ();
+						_keyboardStrokes.RemoveAt (0);
+						if (_keyboardStrokes.Count == 0) {
+							_outputStarted = false;
+							Application.Invoke (() => {
+								tvOutput.ReadOnly = true;
+								tvInput.SetFocus ();
+							});
 						}
-						//System.Diagnostics.Debug.WriteLine ($"_outputStarted: {_outputStarted}");
-					});
-				}
-			};
-
-			btnInput.Clicked += (s, e) => {
-				if (!tvInput.HasFocus && _keyboardStrokes.Count == 0) {
-					tvInput.SetFocus ();
-				}
-			};
-
-			btnOutput.Clicked += (s, e) => {
-				if (!tvOutput.HasFocus && _keyboardStrokes.Count == 0) {
-					tvOutput.SetFocus ();
-				}
-			};
-
-			tvInput.SetFocus ();
-
-			void Win_LayoutComplete (object sender, LayoutEventArgs obj)
-			{
-				inputHorizontalRuler.Text = outputHorizontalRuler.Text = ruler.Repeat ((int)Math.Ceiling ((double)(inputHorizontalRuler.Bounds.Width) / (double)ruler.Length)) [0..(inputHorizontalRuler.Bounds.Width)];
-				inputVerticalRuler.Height = tvInput.Frame.Height + 1;
-				inputVerticalRuler.Text = ruler.Repeat ((int)Math.Ceiling ((double)(inputVerticalRuler.Bounds.Height) / (double)ruler.Length)) [0..(inputVerticalRuler.Bounds.Height)];
-				outputVerticalRuler.Text = ruler.Repeat ((int)Math.Ceiling ((double)(outputVerticalRuler.Bounds.Height) / (double)ruler.Length)) [0..(outputVerticalRuler.Bounds.Height)];
+					}
+					//System.Diagnostics.Debug.WriteLine ($"_outputStarted: {_outputStarted}");
+				});
 			}
+		};
 
-			Win.LayoutComplete += Win_LayoutComplete;
-		}
+		btnInput.Clicked += (s, e) => {
+			if (!tvInput.HasFocus && _keyboardStrokes.Count == 0) {
+				tvInput.SetFocus ();
+			}
+		};
 
-		private void AddKeyboardStrokes (Key e)
+		btnOutput.Clicked += (s, e) => {
+			if (!tvOutput.HasFocus && _keyboardStrokes.Count == 0) {
+				tvOutput.SetFocus ();
+			}
+		};
+
+		tvInput.SetFocus ();
+
+		void Win_LayoutComplete (object sender, LayoutEventArgs obj)
 		{
-			var keyChar = (int)e.KeyCode;
-			var mK = (int)(e.KeyCode & (KeyCode.AltMask | KeyCode.CtrlMask | KeyCode.ShiftMask));
-			keyChar &= ~mK;
-			_keyboardStrokes.Add (keyChar);
+			inputHorizontalRuler.Text = outputHorizontalRuler.Text = ruler.Repeat ((int)Math.Ceiling ((double)inputHorizontalRuler.Bounds.Width / (double)ruler.Length)) [0..inputHorizontalRuler.Bounds.Width];
+			inputVerticalRuler.Height = tvInput.Frame.Height + 1;
+			inputVerticalRuler.Text = ruler.Repeat ((int)Math.Ceiling ((double)inputVerticalRuler.Bounds.Height / (double)ruler.Length)) [0..inputVerticalRuler.Bounds.Height];
+			outputVerticalRuler.Text = ruler.Repeat ((int)Math.Ceiling ((double)outputVerticalRuler.Bounds.Height / (double)ruler.Length)) [0..outputVerticalRuler.Bounds.Height];
 		}
+
+		Win.LayoutComplete += Win_LayoutComplete;
+	}
+
+	void AddKeyboardStrokes (Key e)
+	{
+		int keyChar = (int)e.KeyCode;
+		int mK = (int)(e.KeyCode & (KeyCode.AltMask | KeyCode.CtrlMask | KeyCode.ShiftMask));
+		keyChar &= ~mK;
+		_keyboardStrokes.Add (keyChar);
 	}
-}
+}

+ 31 - 16
UnitTests/Input/KeyTests.cs

@@ -27,6 +27,18 @@ public class KeyTests {
 		Assert.Equal (key, eventArgs.KeyCode);
 	}
 
+	// IsValid
+	[Theory]
+	[InlineData (KeyCode.A, true)]
+	[InlineData (KeyCode.B, true)]
+	[InlineData (KeyCode.F1 | KeyCode.ShiftMask, true)]
+	[InlineData (KeyCode.Null, false)]
+	[InlineData (KeyCode.ShiftMask, false)]
+	[InlineData (KeyCode.CtrlMask, false)]
+	[InlineData (KeyCode.AltMask, false)]
+	[InlineData (KeyCode.ShiftMask | KeyCode.AltMask, false)]
+	public void IsValid (Key key, bool expected) => Assert.Equal (expected, key.IsValid);
+
 	[Fact]
 	public void HandledProperty_ShouldBeFalseByDefault ()
 	{
@@ -243,22 +255,25 @@ public class KeyTests {
 	[InlineData (KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CursorUp, "Alt+Shift+CursorUp")]
 	[InlineData (KeyCode.AltMask | KeyCode.CtrlMask | KeyCode.CursorUp, "Ctrl+Alt+CursorUp")]
 	[InlineData (KeyCode.ShiftMask | KeyCode.CtrlMask | KeyCode.AltMask | KeyCode.CursorUp, "Ctrl+Alt+Shift+CursorUp")]
-	[InlineData (KeyCode.Unknown, "Unknown")]
-	[InlineData (KeyCode.ShiftMask | KeyCode.Unknown, "Shift+Unknown")]
-	[InlineData (KeyCode.CtrlMask | KeyCode.Unknown, "Ctrl+Unknown")]
-	[InlineData (KeyCode.AltMask | KeyCode.Unknown, "Alt+Unknown")]
-	[InlineData (KeyCode.ShiftMask | KeyCode.CtrlMask | KeyCode.Unknown, "Ctrl+Shift+Unknown")]
-	[InlineData (KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.Unknown, "Alt+Shift+Unknown")]
-	[InlineData (KeyCode.AltMask | KeyCode.CtrlMask | KeyCode.Unknown, "Ctrl+Alt+Unknown")]
-	[InlineData (KeyCode.ShiftMask | KeyCode.CtrlMask | KeyCode.AltMask | KeyCode.Unknown, "Ctrl+Alt+Shift+Unknown")]
-	[InlineData (KeyCode.Null, "")]
-	[InlineData (KeyCode.ShiftMask | KeyCode.Null, "Shift")]
-	[InlineData (KeyCode.CtrlMask | KeyCode.Null, "Ctrl")]
-	[InlineData (KeyCode.AltMask | KeyCode.Null, "Alt")]
-	[InlineData (KeyCode.ShiftMask | KeyCode.CtrlMask | KeyCode.Null, "Ctrl+Shift")]
-	[InlineData (KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.Null, "Alt+Shift")]
-	[InlineData (KeyCode.AltMask | KeyCode.CtrlMask | KeyCode.Null, "Ctrl+Alt")]
-	[InlineData (KeyCode.ShiftMask | KeyCode.CtrlMask | KeyCode.AltMask | KeyCode.Null, "Ctrl+Alt+Shift")]
+	[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.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")]
 	[InlineData (KeyCode.CharMask, "CharMask")]
 	[InlineData (KeyCode.SpecialMask, "Ctrl+Alt+Shift")]
 	public void ToString_ShouldReturnFormattedString (KeyCode key, string expected) => Assert.Equal (expected, Key.ToString (key));

+ 3 - 3
UnitTests/Input/ResponderTests.cs

@@ -25,8 +25,8 @@ public class ResponderTests {
 		var r = new View ();
 
 		//Assert.False (r.OnKeyDown (new KeyEventArgs () { Key = Key.Unknown }));
-		Assert.False (r.OnKeyDown (new Key () { KeyCode = KeyCode.Unknown }));
-		Assert.False (r.OnKeyUp (new Key () { KeyCode = KeyCode.Unknown }));
+		Assert.False (r.OnKeyDown (new Key () { KeyCode = KeyCode.Null }));
+		Assert.False (r.OnKeyUp (new Key () { KeyCode = KeyCode.Null }));
 		Assert.False (r.MouseEvent (new MouseEvent () { Flags = MouseFlags.AllEvents }));
 		Assert.False (r.OnMouseEnter (new MouseEvent () { Flags = MouseFlags.AllEvents }));
 		Assert.False (r.OnMouseLeave (new MouseEvent () { Flags = MouseFlags.AllEvents }));
@@ -46,7 +46,7 @@ public class ResponderTests {
 	public void KeyPressed_Handled_True_Cancels_KeyPress ()
 	{
 		var r = new View ();
-		var args = new Key () { KeyCode = KeyCode.Unknown };
+		var args = new Key () { KeyCode = KeyCode.Null };
 
 		Assert.False (r.OnKeyDown (args));
 		Assert.False (args.Handled);

+ 1 - 1
UnitTests/UICatalog/ScenarioTests.cs

@@ -31,7 +31,7 @@ namespace UICatalog.Tests {
 			// Put a QuitKey in at the end
 			FakeConsole.PushMockKeyPress ((KeyCode)Application.QuitKey);
 			foreach (var c in input.Reverse ()) {
-				KeyCode key = KeyCode.Unknown;
+				KeyCode key = KeyCode.Null;
 				if (char.IsLetter (c)) {
 					key = (KeyCode)char.ToUpper (c) | (char.IsUpper (c) ? KeyCode.ShiftMask : (KeyCode)0);
 				} else {

+ 1 - 1
UnitTests/View/HotKeyTests.cs

@@ -200,7 +200,7 @@ public class HotKeyTests {
 	[InlineData (KeyCode.Space)]
 	[InlineData (KeyCode.CursorLeft)]
 	[InlineData (KeyCode.F1)]
-	[InlineData (KeyCode.Unknown)]
+	[InlineData (KeyCode.Null | KeyCode.ShiftMask)]
 	public void Set_Throws_With_Invalid_Key (KeyCode key)
 	{
 		var view = new View ();

+ 2 - 2
UnitTests/View/ViewTests.cs

@@ -125,9 +125,9 @@ namespace Terminal.Gui.ViewTests {
 		{
 			var r = new View ();
 
-			Assert.False (r.OnKeyDown (new Key () { KeyCode = KeyCode.Unknown }));
+			Assert.False (r.OnKeyDown (new Key () { KeyCode = KeyCode.Null }));
 			//Assert.False (r.OnKeyDown (new KeyEventArgs () { Key = Key.Unknown }));
-			Assert.False (r.OnKeyUp (new Key () { KeyCode = KeyCode.Unknown }));
+			Assert.False (r.OnKeyUp (new Key () { KeyCode = KeyCode.Null }));
 			Assert.False (r.MouseEvent (new MouseEvent () { Flags = MouseFlags.AllEvents }));
 			Assert.False (r.OnMouseEnter (new MouseEvent () { Flags = MouseFlags.AllEvents }));
 			Assert.False (r.OnMouseLeave (new MouseEvent () { Flags = MouseFlags.AllEvents }));