浏览代码

Added wide improvements on keys managements.

BDisp 4 年之前
父节点
当前提交
01d4b4f55f

+ 15 - 15
Example/demo.cs

@@ -247,9 +247,9 @@ static class Demo {
 				new MenuItem ("_Close", "", () => { if (Quit ()) { running = MainApp; Application.RequestStop (); } }, null, null, Key.AltMask | Key.F4),
 			}),
 			new MenuBarItem ("_Edit", new MenuItem [] {
-				new MenuItem ("_Copy", "", null, null, null, Key.ControlC),
-				new MenuItem ("C_ut", "", null, null, null, Key.ControlX),
-				new MenuItem ("_Paste", "", null, null, null, Key.ControlV)
+				new MenuItem ("_Copy", "", null, null, null, Key.C | Key.CtrlMask),
+				new MenuItem ("C_ut", "", null, null, null, Key.X | Key.CtrlMask),
+				new MenuItem ("_Paste", "", null, null, null, Key.V | Key.CtrlMask)
 			}),
 		});
 		ntop.Add (menu);
@@ -597,19 +597,19 @@ static class Demo {
 
 		menu = new MenuBar (new MenuBarItem [] {
 			new MenuBarItem ("_File", new MenuItem [] {
-				new MenuItem ("Text _Editor Demo", "", () => { running = Editor; Application.RequestStop (); }, null, null, Key.AltMask | Key.ControlD),
-				new MenuItem ("_New", "Creates new file", NewFile, null, null, Key.AltMask | Key.ControlN),
-				new MenuItem ("_Open", "", Open, null, null, Key.AltMask | Key.ControlO),
-				new MenuItem ("_Hex", "", () => { running = ShowHex; Application.RequestStop (); }, null, null, Key.AltMask | Key.ControlH),
+				new MenuItem ("Text _Editor Demo", "", () => { running = Editor; Application.RequestStop (); }, null, null, Key.AltMask | Key.CtrlMask | Key.D),
+				new MenuItem ("_New", "Creates new file", NewFile, null, null, Key.AltMask | Key.CtrlMask| Key.N),
+				new MenuItem ("_Open", "", Open, null, null, Key.AltMask | Key.CtrlMask| Key.O),
+				new MenuItem ("_Hex", "", () => { running = ShowHex; Application.RequestStop (); }, null, null, Key.AltMask | Key.CtrlMask | Key.H),
 				new MenuItem ("_Close", "", Close, null, null, Key.AltMask | Key.F4),
 				new MenuItem ("_Disabled", "", () => { }, () => false),
 				null,
-				new MenuItem ("_Quit", "", () => { if (Quit ()) { running = null; top.Running = false; } }, null, null, Key.ControlQ)
+				new MenuItem ("_Quit", "", () => { if (Quit ()) { running = null; top.Running = false; } }, null, null, Key.CtrlMask | Key.Q)
 			}),
 			new MenuBarItem ("_Edit", new MenuItem [] {
-				new MenuItem ("_Copy", "", Copy, null, null, Key.AltMask | Key.ControlC),
-				new MenuItem ("C_ut", "", Cut, null, null, Key.AltMask | Key.ControlX),
-				new MenuItem ("_Paste", "", Paste, null, null, Key.AltMask | Key.ControlV),
+				new MenuItem ("_Copy", "", Copy, null, null, Key.AltMask | Key.CtrlMask | Key.C),
+				new MenuItem ("C_ut", "", Cut, null, null, Key.AltMask | Key.CtrlMask| Key.X),
+				new MenuItem ("_Paste", "", Paste, null, null, Key.AltMask | Key.CtrlMask| Key.V),
 				new MenuBarItem ("_Find and Replace",
 					new MenuItem [] { menuItems [0], menuItems [1] }),
 				menuItems[3]
@@ -620,8 +620,8 @@ static class Demo {
 				new MenuItem ("Search Single Item", "", ComboBoxDemo, null, null, Key.AltMask + 2.ToString () [0])
 			}),
 			new MenuBarItem ("A_ssorted", new MenuItem [] {
-				new MenuItem ("_Show text alignments", "", () => ShowTextAlignments (), null, null, Key.AltMask | Key.ControlG),
-				new MenuItem ("_OnKeyDown/Press/Up", "", () => OnKeyDownPressUpDemo (), null, null, Key.AltMask | Key.ControlK)
+				new MenuItem ("_Show text alignments", "", () => ShowTextAlignments (), null, null, Key.AltMask | Key.CtrlMask | Key.G),
+				new MenuItem ("_OnKeyDown/Press/Up", "", () => OnKeyDownPressUpDemo (), null, null, Key.AltMask | Key.CtrlMask | Key.K)
 			}),
 			new MenuBarItem ("_Test Menu and SubMenus", new MenuBarItem [] {
 				new MenuBarItem ("SubMenu1Item_1",  new MenuBarItem [] {
@@ -662,7 +662,7 @@ static class Demo {
 			new StatusItem(Key.F1, "~F1~ Help", () => Help()),
 			new StatusItem(Key.F2, "~F2~ Load", Load),
 			new StatusItem(Key.F3, "~F3~ Save", Save),
-			new StatusItem(Key.ControlQ, "~^Q~ Quit", () => { if (Quit ()) { running = null; top.Running = false; } })
+			new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => { if (Quit ()) { running = null; top.Running = false; } })
 		});
 
 		win.Add (drag, dragText);
@@ -689,7 +689,7 @@ static class Demo {
 
 	private static void Win_KeyPress (View.KeyEventEventArgs e)
 	{
-		if (e.KeyEvent.Key == Key.ControlT) {
+		if ((e.KeyEvent.Key & (Key.CtrlMask | Key.T)) != 0) {
 			if (menu.IsMenuOpen)
 				menu.CloseMenu ();
 			else

+ 61 - 35
Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs

@@ -182,6 +182,18 @@ namespace Terminal.Gui {
 			case Curses.ShiftCtrlKeyEnd: return Key.End | Key.ShiftMask | Key.CtrlMask;
 			case Curses.ShiftCtrlKeyNPage: return Key.PageDown | Key.ShiftMask | Key.CtrlMask;
 			case Curses.ShiftCtrlKeyPPage: return Key.PageUp | Key.ShiftMask | Key.CtrlMask;
+			case Curses.ShiftAltKeyUp: return Key.CursorUp | Key.ShiftMask | Key.AltMask;
+			case Curses.ShiftAltKeyDown: return Key.CursorDown | Key.ShiftMask | Key.AltMask;
+			case Curses.ShiftAltKeyLeft: return Key.CursorLeft | Key.ShiftMask | Key.AltMask;
+			case Curses.ShiftAltKeyRight: return Key.CursorRight | Key.ShiftMask | Key.AltMask;
+			case Curses.ShiftAltKeyNPage: return Key.PageDown | Key.ShiftMask | Key.AltMask;
+			case Curses.ShiftAltKeyPPage: return Key.PageUp | Key.ShiftMask | Key.AltMask;
+			case Curses.ShiftAltKeyHome: return Key.Home | Key.ShiftMask | Key.AltMask;
+			case Curses.ShiftAltKeyEnd: return Key.End | Key.ShiftMask | Key.AltMask;
+			case Curses.AltCtrlKeyNPage: return Key.PageDown | Key.AltMask | Key.CtrlMask;
+			case Curses.AltCtrlKeyPPage: return Key.PageUp | Key.AltMask | Key.CtrlMask;
+			case Curses.AltCtrlKeyHome: return Key.Home | Key.AltMask | Key.CtrlMask;
+			case Curses.AltCtrlKeyEnd: return Key.End | Key.AltMask | Key.CtrlMask;
 			default: return Key.Unknown;
 			}
 		}
@@ -457,7 +469,7 @@ namespace Terminal.Gui {
 				return;
 
 			keyModifiers = new KeyModifiers ();
-			Key k;
+			Key k = Key.Null;
 
 			if (code == Curses.KEY_CODE_YES) {
 				if (wch == Curses.KeyResize) {
@@ -485,6 +497,9 @@ namespace Terminal.Gui {
 				} else if (wch >= 313 && wch <= 324) { // Alt+(F1 - F12)
 					wch -= 48;
 					k = Key.AltMask | MapCursesKey (wch);
+				} else if (wch >= 325 && wch <= 327) { // Shift+Alt+(F1 - F3)
+					wch -= 60;
+					k = Key.ShiftMask | Key.AltMask | MapCursesKey (wch);
 				}
 				keyDownHandler (new KeyEvent (k, MapKeyModifiers (k)));
 				keyHandler (new KeyEvent (k, MapKeyModifiers (k)));
@@ -500,7 +515,6 @@ namespace Terminal.Gui {
 
 				if (code == Curses.KEY_CODE_YES) {
 					k = Key.AltMask | MapCursesKey (wch);
-					keyHandler (new KeyEvent (k, MapKeyModifiers (k)));
 				}
 				if (code == 0) {
 					KeyEvent key;
@@ -509,20 +523,15 @@ namespace Terminal.Gui {
 					// Simulates the AltMask itself by pressing Alt + Space.
 					if (wch2 == (int)Key.Space) {
 						k = Key.AltMask;
-						key = new KeyEvent (k, MapKeyModifiers (k));
-					} else if (wch2 - (int)Key.Space >= 'A' && wch2 - (int)Key.Space <= 'Z') {
+					} else if (wch2 - (int)Key.Space >= (uint)Key.A && wch2 - (int)Key.Space <= (uint)Key.Z) {
 						k = (Key)((uint)Key.AltMask + (wch2 - (int)Key.Space));
-						key = new KeyEvent (k, MapKeyModifiers (k));
-					} else if (wch2 >= (uint)Key.ControlA && wch2 <= (uint)Key.ControlZ) {
-						k = (Key)((uint)(Key.AltMask | Key.CtrlMask) + wch2);
-						key = new KeyEvent (k, MapKeyModifiers (k));
-					} else if (wch2 >= '0' && wch2 <= '9') {
-						k = (Key)((uint)Key.AltMask + (uint)Key.D0 + (wch2 - '0'));
-						key = new KeyEvent (k, MapKeyModifiers (k));
+					} else if (wch2 >= (uint)Key.A - 64 && wch2 <= (uint)Key.Z - 64) {
+						k = (Key)((uint)(Key.AltMask | Key.CtrlMask) + (wch2 + 64));
+					} else if (wch2 >= (uint)Key.D0 && wch2 <= (uint)Key.D9) {
+						k = (Key)((uint)Key.AltMask + (uint)Key.D0 + (wch2 - (uint)Key.D0));
 					} else if (wch2 == 27) {
 						k = (Key)wch2;
-						key = new KeyEvent (k, MapKeyModifiers (k));
-					} else if (wch2 == 91) {
+					} else if (wch2 == Curses.KEY_CODE_SEQ) {
 						int [] c = null;
 						while (code == 0) {
 							code = Curses.get_wch (out wch2);
@@ -531,46 +540,62 @@ namespace Terminal.Gui {
 								c [c.Length - 1] = wch2;
 							}
 						}
-						if (c [0] == 49 && c [1] == 59 && c [2] == 55) { // Ctrl+Alt+(F1 - F4)
+						if (c [0] == 49 && c [1] == 59 && c [2] == 55 && c [3] >= 80 && c [3] <= 83) { // Ctrl+Alt+(F1 - F4)
 							wch2 = c [3] + 185;
 							k = Key.CtrlMask | Key.AltMask | MapCursesKey (wch2);
-						} else if (c [0] == 49 && c [2] == 59 && c [3] == 55 && c [4] == 126) { // Ctrl+Alt+(F5 - F8)
+						} else if (c [0] == 49 && c [2] == 59 && c [3] == 55 && c [4] == 126 && c [1] >= 53 && c [1] <= 57) { // Ctrl+Alt+(F5 - F8)
 							wch2 = c [1] == 53 ? c [1] + 216 : c [1] + 215;
 							k = Key.CtrlMask | Key.AltMask | MapCursesKey (wch2);
-						} else if (c [0] == 50 && c [2] == 59 && c [3] == 55 && c [4] == 126) { // Ctrl+Alt+(F9 - F12)
+						} else if (c [0] == 50 && c [2] == 59 && c [3] == 55 && c [4] == 126 && c [1] >= 48 && c [1] <= 52) { // Ctrl+Alt+(F9 - F12)
 							wch2 = c [1] < 51 ? c [1] + 225 : c [1] + 224;
 							k = Key.CtrlMask | Key.AltMask | MapCursesKey (wch2);
-						} else if (c [0] == 49 && c [1] == 59 && c [2] == 56) { // Ctrl+Shift+Alt+(F1 - F4)
+						} else if (c [0] == 49 && c [1] == 59 && c [2] == 56 && c [3] >= 80 && c [3] <= 83) { // Ctrl+Shift+Alt+(F1 - F4)
 							wch2 = c [3] + 185;
 							k = Key.CtrlMask | Key.ShiftMask | Key.AltMask | MapCursesKey (wch2);
-						} else if (c [0] == 49 && c [2] == 59 && c [3] == 56 && c [4] == 126) { // Ctrl+Shift+Alt+(F5 - F8)
+						} else if (c [0] == 49 && c [2] == 59 && c [3] == 56 && c [4] == 126 && c [1] >= 53 && c [1] <= 57) { // Ctrl+Shift+Alt+(F5 - F8)
 							wch2 = c [1] == 53 ? c [1] + 216 : c [1] + 215;
 							k = Key.CtrlMask | Key.ShiftMask | Key.AltMask | MapCursesKey (wch2);
-						} else if (c [0] == 50 && c [2] == 59 && c [3] == 56 && c [4] == 126) {  // Ctrl+Shift+Alt+(F9 - F12)
+						} else if (c [0] == 50 && c [2] == 59 && c [3] == 56 && c [4] == 126 && c [1] >= 48 && c [1] <= 52) {  // Ctrl+Shift+Alt+(F9 - F12)
 							wch2 = c [1] < 51 ? c [1] + 225 : c [1] + 224;
 							k = Key.CtrlMask | Key.ShiftMask | Key.AltMask | MapCursesKey (wch2);
+						} else if (c [0] == 49 && c [1] == 59 && c [2] == 52 && c [3] == 83) {  // Shift+Alt+(F4)
+							wch2 = 268;
+							k = Key.ShiftMask | Key.AltMask | MapCursesKey (wch2);
+						} else if (c [0] == 49 && c [2] == 59 && c [3] == 52 && c [4] == 126 && c [1] >= 53 && c [1] <= 57) {  // Shift+Alt+(F5 - F8)
+							wch2 = c [1] < 55 ? c [1] + 216 : c [1] + 215;
+							k = Key.ShiftMask | Key.AltMask | MapCursesKey (wch2);
+						} else if (c [0] == 50 && c [2] == 59 && c [3] == 52 && c [4] == 126 && c [1] >= 48 && c [1] <= 52) {  // Shift+Alt+(F9 - F12)
+							wch2 = c [1] < 51 ? c [1] + 225 : c [1] + 224;
+							k = Key.ShiftMask | Key.AltMask | MapCursesKey (wch2);
+						} else if (c [0] == 54 && c [1] == 59 && c [2] == 56 && c [3] == 126) {  // Shift+Ctrl+Alt+KeyNPage
+							k = Key.ShiftMask | Key.CtrlMask | Key.AltMask | Key.PageDown;
+						} else if (c [0] == 53 && c [1] == 59 && c [2] == 56 && c [3] == 126) {  // Shift+Ctrl+Alt+KeyPPage
+							k = Key.ShiftMask | Key.CtrlMask | Key.AltMask | Key.PageUp;
+						} else if (c [0] == 49 && c [1] == 59 && c [2] == 56 && c [3] == 72) {  // Shift+Ctrl+Alt+KeyHome
+							k = Key.ShiftMask | Key.CtrlMask | Key.AltMask | Key.Home;
+						} else if (c [0] == 49 && c [1] == 59 && c [2] == 56 && c [3] == 70) {  // Shift+Ctrl+Alt+KeyEnd
+							k = Key.ShiftMask | Key.CtrlMask | Key.AltMask | Key.End;
 						} else {
 							k = MapCursesKey (wch2);
 						}
-						key = new KeyEvent (k, MapKeyModifiers (k));
 					} else {
 						// Unfortunately there are no way to differentiate Ctrl+Alt+alfa and Ctrl+Shift+Alt+alfa.
+						if (((Key)wch2 & Key.CtrlMask) != 0) {
+							keyModifiers.Ctrl = true;
+						}
 						if (wch2 == 0) {
 							k = Key.CtrlMask | Key.AltMask | Key.Space;
+						} else if (wch >= (uint)Key.A && wch <= (uint)Key.Z) {
+							keyModifiers.Shift = true;
+							keyModifiers.Alt = true;
+						} else if (wch2 < 256) {
+							k = (Key)wch2;
+							keyModifiers.Alt = true;
 						} else {
-							if (((Key)wch2).ToString ().Contains ("Control")) {
-								keyModifiers.Ctrl = true;
-							}
-							if (wch2 < 256) {
-								k = Key.AltMask | (Key)wch2;
-							} else {
-								//k = (Key)wch2;
-								//keyModifiers.Alt = true;
-								k = (Key)((uint)(Key.AltMask | Key.CtrlMask) + wch2);
-							}
+							k = (Key)((uint)(Key.AltMask | Key.CtrlMask) + wch2);
 						}
-						key = new KeyEvent (k, MapKeyModifiers (k));
 					}
+					key = new KeyEvent (k, MapKeyModifiers (k));
 					keyDownHandler (key);
 					keyHandler (key);
 				} else {
@@ -586,10 +611,11 @@ namespace Terminal.Gui {
 				k = (Key)wch;
 				if (wch == 0) {
 					k = Key.CtrlMask | Key.Space;
-				} else if (wch >= (uint)Key.ControlA && wch <= (uint)Key.ControlZ) {
-					k = (Key)wch;
-					keyModifiers.Ctrl = true;
-				} else if (wch >= 'A' && wch <= 'Z') {
+				} else if (wch >= (uint)Key.A - 64 && wch <= (uint)Key.Z - 64) {
+					if ((Key)(wch + 64) != Key.J) {
+						k = Key.CtrlMask | (Key)(wch + 64);
+					}
+				} else if (wch >= (uint)Key.A && wch <= (uint)Key.Z) {
 					keyModifiers.Shift = true;
 				}
 				keyDownHandler (new KeyEvent (k, MapKeyModifiers (k)));

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

@@ -52,6 +52,7 @@ namespace Unix.Terminal {
 		public const int COLOR_CYAN = unchecked((int)0x6);
 		public const int COLOR_WHITE = unchecked((int)0x7);
 		public const int KEY_CODE_YES = unchecked((int)0x100);
+		public const int KEY_CODE_SEQ = unchecked((int)0x5b);
 
 		[Flags]
 		public enum Event : long {
@@ -152,7 +153,18 @@ namespace Unix.Terminal {
 		public const int ShiftCtrlKeyPPage = unchecked((int)0x22c + LeftRightUpNPagePPage);
 		public const int ShiftCtrlKeyHome = unchecked((int)0x218 + Home);
 		public const int ShiftCtrlKeyEnd = unchecked((int)0x213 + DownEnd);
-
+		public const int ShiftAltKeyUp = unchecked((int)0x235 + LeftRightUpNPagePPage);
+		public const int ShiftAltKeyDown = unchecked((int)0x20c + DownEnd);
+		public const int ShiftAltKeyLeft = unchecked((int)0x220 + LeftRightUpNPagePPage);
+		public const int ShiftAltKeyRight = unchecked((int)0x22f + LeftRightUpNPagePPage);
+		public const int ShiftAltKeyNPage = unchecked((int)0x225 + LeftRightUpNPagePPage);
+		public const int ShiftAltKeyPPage = unchecked((int)0x22a + LeftRightUpNPagePPage);
+		public const int ShiftAltKeyHome = unchecked((int)0x216 + Home);
+		public const int ShiftAltKeyEnd = unchecked((int)0x211 + DownEnd);
+		public const int AltCtrlKeyNPage = unchecked((int)0x228 + LeftRightUpNPagePPage);
+		public const int AltCtrlKeyPPage = unchecked((int)0x22d + LeftRightUpNPagePPage);
+		public const int AltCtrlKeyHome = unchecked((int)0x219 + Home);
+		public const int AltCtrlKeyEnd = unchecked((int)0x214 + DownEnd);
 		public const int LC_ALL = 6;
 		static public int ColorPair(int n){
 			return 0 + n * 256;

+ 77 - 29
Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs

@@ -310,33 +310,35 @@ namespace Terminal.Gui {
 		{
 			switch (keyInfo.Key) {
 			case ConsoleKey.Escape:
-				return Key.Esc;
+				return MapKeyModifiers (keyInfo, Key.Esc);
 			case ConsoleKey.Tab:
 				return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab;
 			case ConsoleKey.Home:
-				return Key.Home;
+				return MapKeyModifiers (keyInfo, Key.Home);
 			case ConsoleKey.End:
-				return Key.End;
+				return MapKeyModifiers (keyInfo, Key.End);
 			case ConsoleKey.LeftArrow:
-				return Key.CursorLeft;
+				return MapKeyModifiers (keyInfo, Key.CursorLeft);
 			case ConsoleKey.RightArrow:
-				return Key.CursorRight;
+				return MapKeyModifiers (keyInfo, Key.CursorRight);
 			case ConsoleKey.UpArrow:
-				return Key.CursorUp;
+				return MapKeyModifiers (keyInfo, Key.CursorUp);
 			case ConsoleKey.DownArrow:
-				return Key.CursorDown;
+				return MapKeyModifiers (keyInfo, Key.CursorDown);
 			case ConsoleKey.PageUp:
-				return Key.PageUp;
+				return MapKeyModifiers (keyInfo, Key.PageUp);
 			case ConsoleKey.PageDown:
-				return Key.PageDown;
+				return MapKeyModifiers (keyInfo, Key.PageDown);
 			case ConsoleKey.Enter:
-				return Key.Enter;
+				return MapKeyModifiers (keyInfo, Key.Enter);
 			case ConsoleKey.Spacebar:
-				return Key.Space;
+				return MapKeyModifiers (keyInfo, Key.Space);
 			case ConsoleKey.Backspace:
-				return Key.Backspace;
+				return MapKeyModifiers (keyInfo, Key.Backspace);
 			case ConsoleKey.Delete:
-				return Key.Delete;
+				return MapKeyModifiers (keyInfo, Key.DeleteChar);
+			case ConsoleKey.Insert:
+				return MapKeyModifiers (keyInfo, Key.InsertChar);
 
 			case ConsoleKey.Oem1:
 			case ConsoleKey.Oem2:
@@ -351,38 +353,69 @@ namespace Terminal.Gui {
 			case ConsoleKey.OemComma:
 			case ConsoleKey.OemPlus:
 			case ConsoleKey.OemMinus:
+				if (keyInfo.KeyChar == 0)
+					return Key.Unknown;
+
 				return (Key)((uint)keyInfo.KeyChar);
 			}
 
 			var key = keyInfo.Key;
 			if (key >= ConsoleKey.A && key <= ConsoleKey.Z) {
 				var delta = key - ConsoleKey.A;
-				if (keyInfo.Modifiers == ConsoleModifiers.Control)
-					return (Key)((uint)Key.ControlA + delta);
-				if (keyInfo.Modifiers == ConsoleModifiers.Alt)
-					return (Key)(((uint)Key.AltMask) | ((uint)'A' + delta));
-				if (keyInfo.Modifiers == ConsoleModifiers.Shift)
-					return (Key)((uint)'A' + delta);
-				else
-					return (Key)((uint)'a' + delta);
+				if (keyInfo.Modifiers == ConsoleModifiers.Control) {
+					return (Key)(((uint)Key.CtrlMask) | ((uint)Key.A + delta));
+				}
+				if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
+					return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta));
+				}
+				if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
+					if (keyInfo.KeyChar == 0) {
+						return (Key)(((uint)Key.AltMask | (uint)Key.CtrlMask) | ((uint)Key.A + delta));
+					} else {
+						return (Key)((uint)keyInfo.KeyChar);
+					}
+				}
+				return (Key)((uint)keyInfo.KeyChar);
 			}
 			if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) {
 				var delta = key - ConsoleKey.D0;
-				if (keyInfo.Modifiers == ConsoleModifiers.Alt)
-					return (Key)(((uint)Key.AltMask) | ((uint)'0' + delta));
-				if (keyInfo.Modifiers == ConsoleModifiers.Shift)
-					return (Key)((uint)keyInfo.KeyChar);
-				return (Key)((uint)'0' + delta);
+				if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
+					return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta));
+				}
+				if (keyInfo.Modifiers == ConsoleModifiers.Control) {
+					return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta));
+				}
+				if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30) {
+					return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta));
+				}
+				return (Key)((uint)keyInfo.KeyChar);
 			}
-			if (key >= ConsoleKey.F1 && key <= ConsoleKey.F10) {
+			if (key >= ConsoleKey.F1 && key <= ConsoleKey.F12) {
 				var delta = key - ConsoleKey.F1;
+				if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
+					return MapKeyModifiers (keyInfo, (Key)((uint)Key.F1 + delta));
+				}
 
-				return (Key)((int)Key.F1 + delta);
+				return (Key)((uint)Key.F1 + delta);
 			}
+
 			return (Key)(0xffffffff);
 		}
 
-		KeyModifiers keyModifiers = new KeyModifiers ();
+		KeyModifiers keyModifiers;
+
+		private Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key)
+		{
+			Key keyMod = new Key ();
+			if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0)
+				keyMod = Key.ShiftMask;
+			if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0)
+				keyMod |= Key.CtrlMask;
+			if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0)
+				keyMod |= Key.AltMask;
+
+			return keyMod != Key.Null ? keyMod | key : key;
+		}
 
 		/// <summary>
 		/// 
@@ -399,6 +432,21 @@ namespace Terminal.Gui {
 				var map = MapKey (consoleKey);
 				if (map == (Key)0xffffffff)
 					return;
+
+				if (keyModifiers == null)
+					keyModifiers = new KeyModifiers ();
+				switch (consoleKey.Modifiers) {
+				case ConsoleModifiers.Alt:
+					keyModifiers.Alt = true;
+					break;
+				case ConsoleModifiers.Shift:
+					keyModifiers.Shift = true;
+					break;
+				case ConsoleModifiers.Control:
+					keyModifiers.Ctrl = true;
+					break;
+				}
+
 				keyHandler (new KeyEvent (map, keyModifiers));
 				keyUpHandler (new KeyEvent (map, keyModifiers));
 			};

+ 5 - 5
Terminal.Gui/ConsoleDrivers/NetDriver.cs

@@ -299,21 +299,21 @@ namespace Terminal.Gui {
 			if (key >= ConsoleKey.A && key <= ConsoleKey.Z) {
 				var delta = key - ConsoleKey.A;
 				if (keyInfo.Modifiers == ConsoleModifiers.Control)
-					return (Key)((uint)Key.ControlA + delta);
+					return (Key)((uint)Key.A + delta);
 				if (keyInfo.Modifiers == ConsoleModifiers.Alt)
-					return (Key)(((uint)Key.AltMask) | ((uint)'A' + delta));
+					return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta));
 				if (keyInfo.Modifiers == ConsoleModifiers.Shift)
-					return (Key)((uint)'A' + delta);
+					return (Key)((uint)Key.A + delta);
 				else
 					return (Key)((uint)'a' + delta);
 			}
 			if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) {
 				var delta = key - ConsoleKey.D0;
 				if (keyInfo.Modifiers == ConsoleModifiers.Alt)
-					return (Key)(((uint)Key.AltMask) | ((uint)'0' + delta));
+					return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta));
 				if (keyInfo.Modifiers == ConsoleModifiers.Shift)
 					return (Key)((uint)keyInfo.KeyChar);
-				return (Key)((uint)'0' + delta);
+				return (Key)((uint)Key.D0 + delta);
 			}
 			if (key >= ConsoleKey.F1 && key <= ConsoleKey.F10) {
 				var delta = key - ConsoleKey.F1;

+ 21 - 21
Terminal.Gui/ConsoleDrivers/WindowsDriver.cs

@@ -1074,25 +1074,25 @@ namespace Terminal.Gui {
 				return MapKeyModifiers (keyInfo, Key.InsertChar);
 
 			case ConsoleKey.NumPad0:
-				return keyInfoEx.NumLock ? (Key)(uint)'0' : Key.InsertChar;
+				return keyInfoEx.NumLock ? Key.D0 : Key.InsertChar;
 			case ConsoleKey.NumPad1:
-				return keyInfoEx.NumLock ? (Key)(uint)'1' : Key.End;
+				return keyInfoEx.NumLock ? Key.D1 : Key.End;
 			case ConsoleKey.NumPad2:
-				return keyInfoEx.NumLock ? (Key)(uint)'2' : Key.CursorDown;
+				return keyInfoEx.NumLock ? Key.D2 : Key.CursorDown;
 			case ConsoleKey.NumPad3:
-				return keyInfoEx.NumLock ? (Key)(uint)'3' : Key.PageDown;
+				return keyInfoEx.NumLock ? Key.D3 : Key.PageDown;
 			case ConsoleKey.NumPad4:
-				return keyInfoEx.NumLock ? (Key)(uint)'4' : Key.CursorLeft;
+				return keyInfoEx.NumLock ? Key.D4 : Key.CursorLeft;
 			case ConsoleKey.NumPad5:
-				return keyInfoEx.NumLock ? (Key)(uint)'5' : (Key)((uint)keyInfo.KeyChar);
+				return keyInfoEx.NumLock ? Key.D5 : (Key)((uint)keyInfo.KeyChar);
 			case ConsoleKey.NumPad6:
-				return keyInfoEx.NumLock ? (Key)(uint)'6' : Key.CursorRight;
+				return keyInfoEx.NumLock ? Key.D6 : Key.CursorRight;
 			case ConsoleKey.NumPad7:
-				return keyInfoEx.NumLock ? (Key)(uint)'7' : Key.Home;
+				return keyInfoEx.NumLock ? Key.D7 : Key.Home;
 			case ConsoleKey.NumPad8:
-				return keyInfoEx.NumLock ? (Key)(uint)'8' : Key.CursorUp;
+				return keyInfoEx.NumLock ? Key.D8 : Key.CursorUp;
 			case ConsoleKey.NumPad9:
-				return keyInfoEx.NumLock ? (Key)(uint)'9' : Key.PageUp;
+				return keyInfoEx.NumLock ? Key.D9 : Key.PageUp;
 
 			case ConsoleKey.Oem1:
 			case ConsoleKey.Oem2:
@@ -1119,16 +1119,14 @@ namespace Terminal.Gui {
 			if (key >= ConsoleKey.A && key <= ConsoleKey.Z) {
 				var delta = key - ConsoleKey.A;
 				if (keyInfo.Modifiers == ConsoleModifiers.Control) {
-					return (Key)((uint)Key.ControlA + delta);
+					return (Key)(((uint)Key.CtrlMask) | ((uint)Key.A + delta));
 				}
 				if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
-					return (Key)(((uint)Key.AltMask) | ((uint)'A' + delta));
+					return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta));
 				}
 				if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
-					if (keyInfo.KeyChar == 0) {
-						return (Key)(((uint)Key.AltMask | (uint)Key.CtrlMask) | ((uint)Key.ControlA + delta));
-					} else {
-						return (Key)((uint)keyInfo.KeyChar);
+					if (keyInfo.KeyChar == 0 || (keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26)) {
+						return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta));
 					}
 				}
 				//return (Key)((uint)alphaBase + delta);
@@ -1137,13 +1135,15 @@ namespace Terminal.Gui {
 			if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) {
 				var delta = key - ConsoleKey.D0;
 				if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
-					return (Key)(((uint)Key.AltMask) | ((uint)'0' + delta));
+					return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta));
 				}
 				if (keyInfo.Modifiers == ConsoleModifiers.Control) {
-					return (Key)(((uint)Key.CtrlMask) | ((uint)'0' + delta));
+					return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta));
 				}
-				if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30) {
-					return MapKeyModifiers (keyInfo, (Key)((uint)'0' + delta));
+				if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
+					if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30) {
+						return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta));
+					}
 				}
 				return (Key)((uint)keyInfo.KeyChar);
 			}
@@ -1172,7 +1172,7 @@ namespace Terminal.Gui {
 			if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0)
 				keyMod |= Key.AltMask;
 
-			return keyMod != Key.ControlSpace ? keyMod | key : key;
+			return keyMod != Key.Null ? keyMod | key : key;
 		}
 
 		public override void Init (Action terminalResized)

+ 8 - 0
Terminal.Gui/Core/Application.cs

@@ -168,6 +168,14 @@ namespace Terminal.Gui {
 		{
 			if (_initialized && driver == null) return;
 
+			// Used only for start debugging on Unix.
+//#if DEBUG
+//			while (!System.Diagnostics.Debugger.IsAttached) {
+//				System.Threading.Thread.Sleep (100);
+//			}
+//			System.Diagnostics.Debugger.Break ();
+//#endif
+
 			// This supports Unit Tests and the passing of a mock driver/loopdriver
 			if (driver != null) {
 				if (mainLoopDriver == null) {

+ 111 - 104
Terminal.Gui/Core/Event.cs

@@ -46,9 +46,12 @@ namespace Terminal.Gui {
 	/// <para>
 	///   If the <see cref="SpecialMask"/> is set, then the value is that of the special mask,
 	///   otherwise, the value is the one of the lower bits (as extracted by <see cref="CharMask"/>)
+	/// <para>
+	///   Numerics keys are the values between 48 and 57 corresponding to 0 to 9
+	/// </para>
 	/// </para>
 	/// <para>
-	///   Control keys are the values between 1 and 26 corresponding to Control-A to Control-Z
+	///   Upper alpha keys are the values between 65 and 90 corresponding to A to Z
 	/// </para>
 	/// <para>
 	///   Unicode runes are also stored here, the letter 'A" for example is encoded as a value 65 (not surfaced in the enum).
@@ -70,129 +73,170 @@ namespace Terminal.Gui {
 		SpecialMask = 0xfff00000,
 
 		/// <summary>
-		/// The key code for the user pressing Control-spacebar
+		/// The key code representing null or empty
+		/// </summary>
+		Null = 0,
+
+		/// <summary>
+		/// The key code for the user pressing the return key.
+		/// </summary>
+		Enter = '\n',
+
+		/// <summary>
+		/// The key code for the user pressing the escape key
+		/// </summary>
+		Esc = 27,
+
+		/// <summary>
+		/// The key code for the user pressing the space bar
 		/// </summary>
-		ControlSpace = 0,
+		Space = 32,
 
 		/// <summary>
-		/// The key code for the user pressing Control-A
+		/// Digit 0.
+		/// </summary>
+		D0 = 48,
+		/// <summary>
+		/// Digit 1.
+		/// </summary>
+		D1,
+		/// <summary>
+		/// Digit 2.
 		/// </summary>
-		ControlA = 1,
+		D2,
 		/// <summary>
-		/// The key code for the user pressing Control-B
+		/// Digit 3.
 		/// </summary>
-		ControlB,
+		D3,
 		/// <summary>
-		/// The key code for the user pressing Control-C
+		/// Digit 4.
 		/// </summary>
-		ControlC,
+		D4,
 		/// <summary>
-		/// The key code for the user pressing Control-D
+		/// Digit 5.
 		/// </summary>
-		ControlD,
+		D5,
 		/// <summary>
-		/// The key code for the user pressing Control-E
+		/// Digit 6.
 		/// </summary>
-		ControlE,
+		D6,
 		/// <summary>
-		/// The key code for the user pressing Control-F
+		/// Digit 7.
 		/// </summary>
-		ControlF,
+		D7,
 		/// <summary>
-		/// The key code for the user pressing Control-G
+		/// Digit 8.
 		/// </summary>
-		ControlG,
+		D8,
 		/// <summary>
-		/// The key code for the user pressing Control-H
+		/// Digit 9.
 		/// </summary>
-		ControlH,
+		D9,
+
 		/// <summary>
-		/// The key code for the user pressing Control-I (same as the tab key).
+		/// The key code for the user pressing Shift-A
 		/// </summary>
-		ControlI,
+		A = 65,
 		/// <summary>
-		/// The key code for the user pressing Control-J
+		/// The key code for the user pressing Shift-B
 		/// </summary>
-		ControlJ,
+		B,
 		/// <summary>
-		/// The key code for the user pressing Control-K
+		/// The key code for the user pressing Shift-C
 		/// </summary>
-		ControlK,
+		C,
 		/// <summary>
-		/// The key code for the user pressing Control-L
+		/// The key code for the user pressing Shift-D
 		/// </summary>
-		ControlL,
+		D,
 		/// <summary>
-		/// The key code for the user pressing Control-M
+		/// The key code for the user pressing Shift-E
 		/// </summary>
-		ControlM,
+		E,
 		/// <summary>
-		/// The key code for the user pressing Control-N (same as the return key).
+		/// The key code for the user pressing Shift-F
 		/// </summary>
-		ControlN,
+		F,
 		/// <summary>
-		/// The key code for the user pressing Control-O
+		/// The key code for the user pressing Shift-G
 		/// </summary>
-		ControlO,
+		G,
 		/// <summary>
-		/// The key code for the user pressing Control-P
+		/// The key code for the user pressing Shift-H
 		/// </summary>
-		ControlP,
+		H,
 		/// <summary>
-		/// The key code for the user pressing Control-Q
+		/// The key code for the user pressing Shift-I
 		/// </summary>
-		ControlQ,
+		I,
 		/// <summary>
-		/// The key code for the user pressing Control-R
+		/// The key code for the user pressing Shift-J
 		/// </summary>
-		ControlR,
+		J,
 		/// <summary>
-		/// The key code for the user pressing Control-S
+		/// The key code for the user pressing Shift-K
 		/// </summary>
-		ControlS,
+		K,
 		/// <summary>
-		/// The key code for the user pressing Control-T
+		/// The key code for the user pressing Shift-L
 		/// </summary>
-		ControlT,
+		L,
 		/// <summary>
-		/// The key code for the user pressing Control-U
+		/// The key code for the user pressing Shift-M
 		/// </summary>
-		ControlU,
+		M,
 		/// <summary>
-		/// The key code for the user pressing Control-V
+		/// The key code for the user pressing Shift-N
 		/// </summary>
-		ControlV,
+		N,
 		/// <summary>
-		/// The key code for the user pressing Control-W
+		/// The key code for the user pressing Shift-O
 		/// </summary>
-		ControlW,
+		O,
 		/// <summary>
-		/// The key code for the user pressing Control-X
+		/// The key code for the user pressing Shift-P
 		/// </summary>
-		ControlX,
+		P,
 		/// <summary>
-		/// The key code for the user pressing Control-Y
+		/// The key code for the user pressing Shift-Q
 		/// </summary>
-		ControlY,
+		Q,
 		/// <summary>
-		/// The key code for the user pressing Control-Z
+		/// The key code for the user pressing Shift-R
 		/// </summary>
-		ControlZ,
-
+		R,
 		/// <summary>
-		/// The key code for the user pressing the escape key
+		/// The key code for the user pressing Shift-S
 		/// </summary>
-		Esc = 27,
-
+		S,
 		/// <summary>
-		/// The key code for the user pressing the return key.
+		/// The key code for the user pressing Shift-T
 		/// </summary>
-		Enter = '\n',
-
+		T,
 		/// <summary>
-		/// The key code for the user pressing the space bar
+		/// The key code for the user pressing Shift-U
 		/// </summary>
-		Space = 32,
+		U,
+		/// <summary>
+		/// The key code for the user pressing Shift-V
+		/// </summary>
+		V,
+		/// <summary>
+		/// The key code for the user pressing Shift-W
+		/// </summary>
+		W,
+		/// <summary>
+		/// The key code for the user pressing Shift-X
+		/// </summary>
+		X,
+		/// <summary>
+		/// The key code for the user pressing Shift-Y
+		/// </summary>
+		Y,
+		/// <summary>
+		/// The key code for the user pressing Shift-Z
+		/// </summary>
+		Z,
 
 		/// <summary>
 		/// The key code for the user pressing the delete key.
@@ -261,6 +305,7 @@ namespace Terminal.Gui {
 		/// Insert character key
 		/// </summary>
 		InsertChar,
+
 		/// <summary>
 		/// F1 key.
 		/// </summary>
@@ -309,6 +354,7 @@ namespace Terminal.Gui {
 		/// F12 key.
 		/// </summary>
 		F12,
+
 		/// <summary>
 		/// The key code for the user pressing the tab key (forwards tab key).
 		/// </summary>
@@ -317,46 +363,7 @@ namespace Terminal.Gui {
 		/// Shift-tab key (backwards tab key).
 		/// </summary>
 		BackTab,
-		/// <summary>
-		/// Digit 0.
-		/// </summary>
-		D0 = 48,
-		/// <summary>
-		/// Digit 1.
-		/// </summary>
-		D1,
-		/// <summary>
-		/// Digit 2.
-		/// </summary>
-		D2,
-		/// <summary>
-		/// Digit 3.
-		/// </summary>
-		D3,
-		/// <summary>
-		/// Digit 4.
-		/// </summary>
-		D4,
-		/// <summary>
-		/// Digit 5.
-		/// </summary>
-		D5,
-		/// <summary>
-		/// Digit 6.
-		/// </summary>
-		D6,
-		/// <summary>
-		/// Digit 7.
-		/// </summary>
-		D7,
-		/// <summary>
-		/// Digit 8.
-		/// </summary>
-		D8,
-		/// <summary>
-		/// Digit 9.
-		/// </summary>
-		D9,
+
 		/// <summary>
 		/// A key with an unknown mapping was raised.
 		/// </summary>

+ 206 - 0
Terminal.Gui/Core/ShortCutHelper.cs

@@ -0,0 +1,206 @@
+using NStack;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Terminal.Gui {
+	/// <summary>
+	/// Represents a helper to manipulate shortcut keys used on views.
+	/// </summary>
+	public class ShortCutHelper {
+		private Key shortCut;
+
+		/// <summary>
+		/// This is the global setting that can be used as a global shortcut to invoke the action on the view.
+		/// </summary>
+		public virtual Key ShortCut {
+			get => shortCut;
+			set {
+				if (shortCut != value && (PostShortCutValidation (value) || value == Key.Null)) {
+					shortCut = value;
+				}
+			}
+		}
+
+		/// <summary>
+		/// The keystroke combination used in the <see cref="ShortCut"/> as string.
+		/// </summary>
+		public virtual ustring ShortCutTag => GetShortCutTag (shortCut);
+
+		/// <summary>
+		/// Get the <see cref="ShortCut"/> key as string.
+		/// </summary>
+		/// <param name="shortCut">The shortcut key.</param>
+		/// <returns></returns>
+		public static ustring GetShortCutTag (Key shortCut)
+		{
+			if (shortCut == Key.Null) {
+				return "";
+			}
+
+			var k = shortCut;
+			var delimiter = MenuBar.ShortCutDelimiter;
+			ustring tag = ustring.Empty;
+			var sCut = GetKeyToString (k, out Key knm).ToString ();
+			if (knm == Key.Unknown) {
+				k &= ~Key.Unknown;
+				sCut = GetKeyToString (k, out _).ToString ();
+			}
+			if ((k & Key.CtrlMask) != 0) {
+				tag = "Ctrl";
+			}
+			if ((k & Key.ShiftMask) != 0) {
+				if (!tag.IsEmpty) {
+					tag += delimiter;
+				}
+				tag += "Shift";
+			}
+			if ((k & Key.AltMask) != 0) {
+				if (!tag.IsEmpty) {
+					tag += delimiter;
+				}
+				tag += "Alt";
+			}
+
+			ustring [] keys = ustring.Make (sCut).Split (",");
+			for (int i = 0; i < keys.Length; i++) {
+				var key = keys [i].TrimSpace ();
+				if (key == Key.AltMask.ToString () || key == Key.ShiftMask.ToString () || key == Key.CtrlMask.ToString ()) {
+					continue;
+				}
+				if (!tag.IsEmpty) {
+					tag += delimiter;
+				}
+				if (!key.Contains ("F") && key.Length > 2 && keys.Length == 1) {
+					k = (uint)Key.AltMask + k;
+					tag += ((char)k).ToString ();
+				} else if (key.Length == 2 && key.StartsWith ("D")) {
+					tag += ((char)key.ElementAt (1)).ToString ();
+				} else {
+					tag += key;
+				}
+			}
+
+			return tag;
+		}
+
+		/// <summary>
+		/// Return key as string.
+		/// </summary>
+		/// <param name="key">The key to extract.</param>
+		/// <param name="knm">Correspond to the non modifier key.</param>
+		public static ustring GetKeyToString (Key key, out Key knm)
+		{
+			if (key == Key.Null) {
+				knm = Key.Null;
+				return "";
+			}
+
+			knm = key;
+			var mK = key & (Key.AltMask | Key.CtrlMask | Key.ShiftMask);
+			knm &= ~mK;
+			for (uint i = (uint)Key.F1; i < (uint)Key.F12; i++) {
+				if (knm == (Key)i) {
+					mK |= (Key)i;
+				}
+			}
+			knm &= ~mK;
+			uint.TryParse (knm.ToString (), out uint c);
+			var s = mK == Key.Null ? "" : mK.ToString ();
+			if (s != "" && (knm != Key.Null || c > 0)) {
+				s += ",";
+			}
+			s += c == 0 ? knm == Key.Null ? "" : knm.ToString () : ((char)c).ToString ();
+			return s;
+		}
+
+		/// <summary>
+		/// Allows to retrieve a <see cref="Key"/> from a <see cref="ShortCutTag"/>
+		/// </summary>
+		/// <param name="tag">The key as string.</param>
+		public static Key GetShortCutFromTag (ustring tag)
+		{
+			var sCut = tag;
+			if (sCut.IsEmpty) {
+				return default;
+			}
+
+			Key key = Key.Null;
+			//var hasCtrl = false;
+			var delimiter = MenuBar.ShortCutDelimiter;
+
+			ustring [] keys = sCut.Split (delimiter);
+			for (int i = 0; i < keys.Length; i++) {
+				var k = keys [i];
+				if (k == "Ctrl") {
+					//hasCtrl = true;
+					key |= Key.CtrlMask;
+				} else if (k == "Shift") {
+					key |= Key.ShiftMask;
+				} else if (k == "Alt") {
+					key |= Key.AltMask;
+				} else if (k.StartsWith ("F") && k.Length > 1) {
+					int.TryParse (k.Substring (1).ToString (), out int n);
+					for (uint j = (uint)Key.F1; j <= (uint)Key.F12; j++) {
+						int.TryParse (((Key)j).ToString ().Substring (1), out int f);
+						if (f == n) {
+							key |= (Key)j;
+						}
+					}
+				} else {
+					key |= (Key)Enum.Parse (typeof (Key), k.ToString ());
+				}
+			}
+
+			return key;
+		}
+
+		/// <summary>
+		/// Lookup for a <see cref="Key"/> on range of keys.
+		/// </summary>
+		/// <param name="key">The source key.</param>
+		/// <param name="first">First key in range.</param>
+		/// <param name="last">Last key in range.</param>
+		public static bool CheckKeysFlagRange (Key key, Key first, Key last)
+		{
+			for (uint i = (uint)first; i < (uint)last; i++) {
+				if ((key | (Key)i) == key) {
+					return true;
+				}
+			}
+			return false;
+		}
+
+		/// <summary>
+		/// Used at key down or key press validation.
+		/// </summary>
+		/// <param name="key">The key to validate.</param>
+		/// <returns><c>true</c> if is valid.<c>false</c>otherwise.</returns>
+		public static bool PreShortCutValidation (Key key)
+		{
+			if ((key & (Key.CtrlMask | Key.ShiftMask | Key.AltMask)) == 0 && !CheckKeysFlagRange (key, Key.F1, Key.F12)) {
+				return false;
+			}
+
+			return true;
+		}
+
+		/// <summary>
+		/// Used at key up validation.
+		/// </summary>
+		/// <param name="key">The key to validate.</param>
+		/// <returns><c>true</c> if is valid.<c>false</c>otherwise.</returns>
+		public static bool PostShortCutValidation (Key key)
+		{
+			GetKeyToString (key, out Key knm);
+
+			if (CheckKeysFlagRange (key, Key.F1, Key.F12) ||
+				((key & (Key.CtrlMask | Key.ShiftMask | Key.AltMask)) != 0 && knm != Key.Null && knm != Key.Unknown)) {
+				return true;
+			}
+			return false;
+		}
+	}
+}

+ 4 - 4
Terminal.Gui/Core/Toplevel.cs

@@ -164,11 +164,11 @@ namespace Terminal.Gui {
 				return true;
 
 			switch (keyEvent.Key) {
-			case Key.ControlQ:
+			case Key.Q | Key.CtrlMask:
 				// FIXED: stop current execution of this container
 				Application.RequestStop ();
 				break;
-			case Key.ControlZ:
+			case Key.Z | Key.CtrlMask:
 				Driver.Suspend ();
 				return true;
 
@@ -181,7 +181,7 @@ namespace Terminal.Gui {
 			case Key.Tab:
 			case Key.CursorRight:
 			case Key.CursorDown:
-			case Key.ControlI: // Unix
+			case Key.I | Key.CtrlMask: // Unix
 				var old = GetDeepestFocusedSubview (Focused);
 				if (!FocusNext ())
 					FocusNext ();
@@ -206,7 +206,7 @@ namespace Terminal.Gui {
 				}
 				return true;
 
-			case Key.ControlL:
+			case Key.L | Key.CtrlMask:
 				Application.Refresh ();
 				return true;
 			}

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

@@ -357,7 +357,7 @@ namespace Terminal.Gui {
 			}
 
 			// Unix emulation
-			if (e.Key == Key.ControlU) {
+			if (e.Key == (Key.U | Key.CtrlMask)) {
 				Reset ();
 				return true;
 			}

+ 5 - 5
Terminal.Gui/Views/DateField.cs

@@ -299,7 +299,7 @@ namespace Terminal.Gui {
 		{
 			switch (kb.Key) {
 			case Key.DeleteChar:
-			case Key.ControlD:
+			case Key.D | Key.CtrlMask:
 				if (ReadOnly)
 					return true;
 
@@ -317,22 +317,22 @@ namespace Terminal.Gui {
 
 			// Home, C-A
 			case Key.Home:
-			case Key.ControlA:
+			case Key.A | Key.CtrlMask:
 				CursorPosition = 1;
 				break;
 
 			case Key.CursorLeft:
-			case Key.ControlB:
+			case Key.B | Key.CtrlMask:
 				DecCursorPosition ();
 				break;
 
 			case Key.End:
-			case Key.ControlE: // End
+			case Key.E | Key.CtrlMask: // End
 				CursorPosition = FieldLen;
 				break;
 
 			case Key.CursorRight:
-			case Key.ControlF:
+			case Key.F | Key.CtrlMask:
 				IncCursorPosition ();
 				break;
 

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

@@ -325,7 +325,7 @@ namespace Terminal.Gui {
 			case Key.PageUp:
 				MoveUp (bytesPerLine * Frame.Height);
 				break;
-			case Key.ControlV:
+			case Key.V | Key.CtrlMask:
 			case Key.PageDown:
 				MoveDown (bytesPerLine * Frame.Height);
 				break;

+ 5 - 5
Terminal.Gui/Views/ListView.cs

@@ -348,14 +348,14 @@ namespace Terminal.Gui {
 
 			switch (kb.Key) {
 			case Key.CursorUp:
-			case Key.ControlP:
+			case Key.P | Key.CtrlMask:
 				return MoveUp ();
 
 			case Key.CursorDown:
-			case Key.ControlN:
+			case Key.N | Key.CtrlMask:
 				return MoveDown ();
 
-			case Key.ControlV:
+			case Key.V | Key.CtrlMask:
 			case Key.PageDown:
 				return MovePageDown ();
 
@@ -464,7 +464,7 @@ namespace Terminal.Gui {
 		public virtual bool MoveDown ()
 		{
 			if (source.Count == 0) {
-				// Do we set lastSelectedItem to zero here?
+				// Do we set lastSelectedItem to -1 here?
 				return false; //Nothing for us to move to
 			}
 			if (selected >= source.Count) {
@@ -496,7 +496,7 @@ namespace Terminal.Gui {
 		public virtual bool MoveUp ()
 		{
 			if (source.Count == 0) {
-				// Do we set lastSelectedItem to zero here?
+				// Do we set lastSelectedItem to -1 here?
 				return false; //Nothing for us to move to
 			}
 			if (selected >= source.Count) {

+ 19 - 151
Terminal.Gui/Views/Menu.cs

@@ -41,17 +41,19 @@ namespace Terminal.Gui {
 	/// </summary>
 	public class MenuItem {
 		ustring title;
-		Key shortCut;
+
+		ShortCutHelper shortCutHelper;
 
 		/// <summary>
 		/// Initializes a new instance of <see cref="MenuItem"/>
 		/// </summary>
-		public MenuItem (Key shortCut = Key.ControlSpace)
+		public MenuItem (Key shortCut = Key.Null)
 		{
 			Title = "";
 			Help = "";
-			if (shortCut != Key.ControlSpace) {
-				ShortCut = shortCut;
+			shortCutHelper = new ShortCutHelper ();
+			if (shortCut != Key.Null) {
+				shortCutHelper.ShortCut = shortCut;
 			}
 		}
 
@@ -64,15 +66,16 @@ namespace Terminal.Gui {
 		/// <param name="canExecute">Function to determine if the action can currently be executed.</param>
 		/// <param name="parent">The <see cref="Parent"/> of this menu item.</param>
 		/// <param name="shortCut">The <see cref="ShortCut"/> keystroke combination.</param>
-		public MenuItem (ustring title, ustring help, Action action, Func<bool> canExecute = null, MenuItem parent = null, Key shortCut = Key.ControlSpace)
+		public MenuItem (ustring title, ustring help, Action action, Func<bool> canExecute = null, MenuItem parent = null, Key shortCut = Key.Null)
 		{
 			Title = title ?? "";
 			Help = help ?? "";
 			Action = action;
 			CanExecute = canExecute;
 			Parent = parent;
-			if (shortCut != Key.ControlSpace) {
-				ShortCut = shortCut;
+			shortCutHelper = new ShortCutHelper ();
+			if (shortCut != Key.Null) {
+				shortCutHelper.ShortCut = shortCut;
 			}
 		}
 
@@ -84,25 +87,21 @@ namespace Terminal.Gui {
 		public Rune HotKey;
 
 		/// <summary>
-		/// This is the global setting that can be used as a global shortcut to invoke the action on the menu.
+		/// This is the global setting that can be used as a global <see cref="ShortCutHelper.ShortCut"/> to invoke the action on the menu.
 		/// </summary>
 		public Key ShortCut {
-			get => shortCut;
+			get => shortCutHelper.ShortCut;
 			set {
-				if (shortCut != value) {
-					if (GetKeyToString (value).Contains ("Control")) {
-						shortCut = Key.CtrlMask | value;
-					} else {
-						shortCut = value;
-					}
+				if (shortCutHelper.ShortCut != value && (ShortCutHelper.PostShortCutValidation (value) || value == Key.Null)) {
+					shortCutHelper.ShortCut = value;
 				}
 			}
 		}
 
 		/// <summary>
-		/// The keystroke combination used in the <see cref="ShortCut"/> as string.
+		/// The keystroke combination used in the <see cref="ShortCutHelper.ShortCutTag"/> as string.
 		/// </summary>
-		public ustring ShortCutTag => GetShortCutTag (shortCut);
+		public ustring ShortCutTag => ShortCutHelper.GetShortCutTag (shortCutHelper.ShortCut);
 
 		/// <summary>
 		/// Gets or sets the title.
@@ -201,137 +200,6 @@ namespace Terminal.Gui {
 				}
 			}
 		}
-
-		/// <summary>
-		/// Get the <see cref="ShortCut"/> key as string.
-		/// </summary>
-		/// <param name="shortCut">The shortcut key.</param>
-		/// <returns></returns>
-		public ustring GetShortCutTag (Key shortCut)
-		{
-			if (shortCut == Key.ControlSpace) {
-				return "";
-			}
-
-			var k = (uint)shortCut;
-			var delimiter = MenuBar.ShortCutDelimiter;
-			ustring tag = ustring.Empty;
-			var sCut = GetKeyToString (shortCut).ToString ();
-			if (sCut.Contains ("Control") || (shortCut & Key.CtrlMask) != 0) {
-				tag = "Ctrl";
-			}
-			if ((shortCut & Key.ShiftMask) != 0) {
-				if (!tag.IsEmpty) {
-					tag += delimiter;
-				}
-				tag += "Shift";
-			}
-			if ((shortCut & Key.AltMask) != 0) {
-				if (!tag.IsEmpty) {
-					tag += delimiter;
-				}
-				tag += "Alt";
-			}
-
-			ustring [] keys = ustring.Make (sCut).Split (",");
-			for (int i = 0; i < keys.Length; i++) {
-				var key = keys [i].TrimSpace ();
-				if (key == Key.AltMask.ToString () || key == Key.ShiftMask.ToString () || key == Key.CtrlMask.ToString ()) {
-					continue;
-				}
-				if (!tag.IsEmpty) {
-					tag += delimiter;
-				}
-				if (key.Contains ("Control")) {
-					tag += ((char)key.ElementAt (7)).ToString ();
-				} else if (!key.Contains ("F") && key.Length > 2 && keys.Length == 1) {
-					k = (uint)Key.AltMask + k;
-					tag += ((char)k).ToString ();
-				} else if (key.Length == 2 && key.StartsWith ("D")) {
-					tag += ((char)key.ElementAt (1)).ToString ();
-				} else {
-					tag += key;
-				}
-			}
-
-			return tag;
-		}
-
-		ustring GetKeyToString (Key key)
-		{
-			if (key == Key.ControlSpace) {
-				return "";
-			}
-
-			var mK = key & (Key.AltMask | Key.CtrlMask | Key.ShiftMask);
-			for (uint i = (uint)Key.F1; i < (uint)Key.F12; i++) {
-				if ((key | (Key)i) == key) {
-					mK |= (Key)i;
-				}
-			}
-			var k = key;
-			k &= ~mK;
-			int.TryParse (k.ToString (), out int c);
-			var s = mK == Key.ControlSpace ? "" : mK.ToString ();
-			if (s != "" && (k != Key.ControlSpace || c > 0)) {
-				s += ",";
-			}
-			s += c == 0 ? k == Key.ControlSpace ? "" : k.ToString () : ((char)c).ToString ();
-			return s;
-		}
-
-		/// <summary>
-		/// Allows to generate a <see cref="Key"/> from a <see cref="ShortCutTag"/>
-		/// </summary>
-		/// <param name="tag">The key as string.</param>
-		/// <returns></returns>
-		public Key CreateShortCutFromTag (ustring tag)
-		{
-			var sCut = tag;
-			if (sCut.IsEmpty) {
-				return default;
-			}
-
-			Key key = Key.ControlSpace;
-			var hasCtrl = false;
-			var delimiter = MenuBar.ShortCutDelimiter;
-
-			ustring [] keys = sCut.Split (delimiter);
-			for (int i = 0; i < keys.Length; i++) {
-				var k = keys [i];
-				if (k == "Ctrl") {
-					hasCtrl = true;
-					key |= Key.CtrlMask;
-				} else if (k == "Shift") {
-					key |= Key.ShiftMask;
-				} else if (k == "Alt") {
-					key |= Key.AltMask;
-				} else if (k.StartsWith ("F") && k.Length > 1) {
-					int.TryParse (k.Substring (1).ToString (), out int n);
-					for (uint j = (uint)Key.F1; j < (uint)Key.F12; j++) {
-						int.TryParse (((Key)j).ToString ().Substring (1), out int f);
-						if (f == n) {
-							key |= (Key)j;
-						}
-					}
-				} else if (k [0] >= 'A' && k [0] <= 'Z') {
-					if (hasCtrl) {
-						var n = k [0] - 'A' + 1;
-						var d = n - (uint)Key.ControlA;
-						key |= (Key)((uint)Key.ControlA + d);
-					} else {
-						key |= (Key)k [0];
-					}
-				} else if (k [0] >= '0' && k [0] <= '9') {
-					//var n = k [0] - (uint)Key.D0 + 1;
-					//var d = n - (uint)Key.D0;
-					//key |= (Key)((uint)Key.D0 + d);
-					key |= (Key)k [0];
-				}
-			}
-
-			return key;
-		}
 	}
 
 	/// <summary>
@@ -513,7 +381,7 @@ namespace Terminal.Gui {
 			} else {
 
 				current = -1;
-				for (int i = 0; i < barItems.Children.Length; i++) {
+				for (int i = 0; i < barItems.Children?.Length; i++) {
 					if (barItems.Children [i] != null) {
 						current = i;
 						break;
@@ -1473,7 +1341,7 @@ namespace Terminal.Gui {
 				if (mi == null) {
 					continue;
 				}
-				if ((!(mi is MenuBarItem mbiTopLevel) || mbiTopLevel.IsTopLevel) && mi.ShortCut != Key.ControlSpace && mi.ShortCut == (Key)key) {
+				if ((!(mi is MenuBarItem mbiTopLevel) || mbiTopLevel.IsTopLevel) && mi.ShortCut != Key.Null && mi.ShortCut == (Key)key) {
 					var action = mi.Action;
 					if (action != null) {
 						Application.MainLoop.AddIdle (() => {
@@ -1548,7 +1416,7 @@ namespace Terminal.Gui {
 				break;
 
 			case Key.Esc:
-			case Key.ControlC:
+			case Key.C | Key.CtrlMask:
 				//TODO: Running = false;
 				CloseMenu ();
 				if (openedByAltKey) {

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

@@ -741,7 +741,7 @@ namespace Terminal.Gui {
 			case Key.PageUp:
 				return ScrollUp (Bounds.Height);
 
-			case Key.ControlV:
+			case Key.V | Key.CtrlMask:
 			case Key.PageDown:
 				return ScrollDown (Bounds.Height);
 

+ 20 - 11
Terminal.Gui/Views/TextField.cs

@@ -347,7 +347,7 @@ namespace Terminal.Gui {
 
 			switch (GetModifiersKey (kb)) {
 			case Key.DeleteChar:
-			case Key.ControlD | Key.CtrlMask:
+			case Key.D | Key.CtrlMask:
 				if (ReadOnly)
 					return true;
 
@@ -381,6 +381,8 @@ namespace Terminal.Gui {
 				break;
 
 			case Key.Home | Key.ShiftMask:
+			case Key.Home | Key.ShiftMask | Key.CtrlMask:
+			case Key.A | Key.ShiftMask | Key.CtrlMask:
 				if (point > 0) {
 					int x = point;
 					point = 0;
@@ -389,6 +391,8 @@ namespace Terminal.Gui {
 				break;
 
 			case Key.End | Key.ShiftMask:
+			case Key.End | Key.ShiftMask | Key.CtrlMask:
+			case Key.E | Key.ShiftMask | Key.CtrlMask:
 				if (point < text.Count) {
 					int x = point;
 					point = text.Count;
@@ -398,7 +402,8 @@ namespace Terminal.Gui {
 
 			// Home, C-A
 			case Key.Home:
-			case Key.ControlA | Key.CtrlMask:
+			case Key.Home | Key.CtrlMask:
+			case Key.A | Key.CtrlMask:
 				ClearAllSelection ();
 				point = 0;
 				Adjust ();
@@ -441,7 +446,7 @@ namespace Terminal.Gui {
 				break;
 
 			case Key.CursorLeft:
-			case Key.ControlB | Key.CtrlMask:
+			case Key.B | Key.CtrlMask:
 				ClearAllSelection ();
 				if (point > 0) {
 					point--;
@@ -450,14 +455,15 @@ namespace Terminal.Gui {
 				break;
 
 			case Key.End:
-			case Key.ControlE | Key.CtrlMask: // End
+			case Key.End | Key.CtrlMask:
+			case Key.E | Key.CtrlMask: // End
 				ClearAllSelection ();
 				point = text.Count;
 				Adjust ();
 				break;
 
 			case Key.CursorRight:
-			case Key.ControlF | Key.CtrlMask:
+			case Key.F | Key.CtrlMask:
 				ClearAllSelection ();
 				if (point == text.Count)
 					break;
@@ -465,7 +471,7 @@ namespace Terminal.Gui {
 				Adjust ();
 				break;
 
-			case Key.ControlK | Key.CtrlMask: // kill-to-end
+			case Key.K | Key.CtrlMask: // kill-to-end
 				if (ReadOnly)
 					return true;
 
@@ -478,7 +484,7 @@ namespace Terminal.Gui {
 				break;
 
 			// Undo
-			case Key.ControlZ | Key.CtrlMask:
+			case Key.Z | Key.CtrlMask:
 				if (ReadOnly)
 					return true;
 
@@ -494,7 +500,7 @@ namespace Terminal.Gui {
 				break;
 
 			//Redo
-			case Key.ControlY | Key.CtrlMask: // Control-y, yank
+			case Key.Y | Key.CtrlMask: // Control-y, yank
 				if (ReadOnly)
 					return true;
 
@@ -530,6 +536,7 @@ namespace Terminal.Gui {
 				break;
 
 			case Key.CursorLeft | Key.CtrlMask:
+			case Key.CursorUp | Key.CtrlMask:
 			case (Key)((int)'b' + Key.AltMask):
 				ClearAllSelection ();
 				int bw = WordBackward (point);
@@ -539,6 +546,7 @@ namespace Terminal.Gui {
 				break;
 
 			case Key.CursorRight | Key.CtrlMask:
+			case Key.CursorDown | Key.CtrlMask:
 			case (Key)((int)'f' + Key.AltMask):
 				ClearAllSelection ();
 				int fw = WordForward (point);
@@ -552,18 +560,18 @@ namespace Terminal.Gui {
 				SetNeedsDisplay ();
 				break;
 
-			case Key.ControlC | Key.CtrlMask:
+			case Key.C | Key.CtrlMask:
 				Copy ();
 				break;
 
-			case Key.ControlX | Key.CtrlMask:
+			case Key.X | Key.CtrlMask:
 				if (ReadOnly)
 					return true;
 
 				Cut ();
 				break;
 
-			case Key.ControlV | Key.CtrlMask:
+			case Key.V | Key.CtrlMask:
 				Paste ();
 				break;
 
@@ -817,6 +825,7 @@ namespace Terminal.Gui {
 			SelectedLength = 0;
 			SelectedText = "";
 			start = 0;
+			SetNeedsDisplay ();
 		}
 
 		void SetSelectedStartSelectedLength ()

+ 15 - 15
Terminal.Gui/Views/TextView.cs

@@ -734,13 +734,13 @@ namespace Terminal.Gui {
 			// Handle some state here - whether the last command was a kill
 			// operation and the column tracking (up/down)
 			switch (kb.Key) {
-			case Key.ControlN:
+			case Key.N | Key.CtrlMask:
 			case Key.CursorDown:
-			case Key.ControlP:
+			case Key.P | Key.CtrlMask:
 			case Key.CursorUp:
 				lastWasKill = false;
 				break;
-			case Key.ControlK:
+			case Key.K | Key.CtrlMask:
 				break;
 			default:
 				lastWasKill = false;
@@ -751,7 +751,7 @@ namespace Terminal.Gui {
 			// Dispatch the command.
 			switch (kb.Key) {
 			case Key.PageDown:
-			case Key.ControlV:
+			case Key.V | Key.CtrlMask:
 				int nPageDnShift = Frame.Height - 1;
 				if (currentRow < model.Count) {
 					if (columnTrack == -1)
@@ -782,17 +782,17 @@ namespace Terminal.Gui {
 				}
 				break;
 
-			case Key.ControlN:
+			case Key.N | Key.CtrlMask:
 			case Key.CursorDown:
 				MoveDown ();
 				break;
 
-			case Key.ControlP:
+			case Key.P | Key.CtrlMask:
 			case Key.CursorUp:
 				MoveUp ();
 				break;
 
-			case Key.ControlF:
+			case Key.F | Key.CtrlMask:
 			case Key.CursorRight:
 				var currentLine = GetCurrentLine ();
 				if (currentColumn < currentLine.Count) {
@@ -817,7 +817,7 @@ namespace Terminal.Gui {
 				}
 				break;
 
-			case Key.ControlB:
+			case Key.B | Key.CtrlMask:
 			case Key.CursorLeft:
 				if (currentColumn > 0) {
 					currentColumn--;
@@ -880,7 +880,7 @@ namespace Terminal.Gui {
 
 			// Home, C-A
 			case Key.Home:
-			case Key.ControlA:
+			case Key.A | Key.CtrlMask:
 				currentColumn = 0;
 				if (currentColumn < leftColumn) {
 					leftColumn = 0;
@@ -889,7 +889,7 @@ namespace Terminal.Gui {
 					PositionCursor ();
 				break;
 			case Key.DeleteChar:
-			case Key.ControlD: // Delete
+			case Key.D | Key.CtrlMask: // Delete
 				if (isReadOnly)
 					break;
 				currentLine = GetCurrentLine ();
@@ -909,7 +909,7 @@ namespace Terminal.Gui {
 				break;
 
 			case Key.End:
-			case Key.ControlE: // End
+			case Key.E | Key.CtrlMask: // End
 				currentLine = GetCurrentLine ();
 				currentColumn = currentLine.Count;
 				int pcol = leftColumn;
@@ -921,7 +921,7 @@ namespace Terminal.Gui {
 				PositionCursor ();
 				break;
 
-			case Key.ControlK: // kill-to-end
+			case Key.K | Key.CtrlMask: // kill-to-end
 				if (isReadOnly)
 					break;
 				currentLine = GetCurrentLine ();
@@ -946,14 +946,14 @@ namespace Terminal.Gui {
 				lastWasKill = true;
 				break;
 
-			case Key.ControlY: // Control-y, yank
+			case Key.Y | Key.CtrlMask: // Control-y, yank
 				if (isReadOnly)
 					break;
 				InsertText (Clipboard.Contents);
 				selecting = false;
 				break;
 
-			case Key.ControlSpace:
+			case Key.Space | Key.CtrlMask:
 				selecting = true;
 				selectionStartColumn = currentColumn;
 				selectionStartRow = currentRow;
@@ -964,7 +964,7 @@ namespace Terminal.Gui {
 				selecting = false;
 				break;
 
-			case Key.ControlW:
+			case Key.W | Key.CtrlMask:
 				SetClipboard (GetRegion ());
 				if (!isReadOnly)
 					ClearRegion ();

+ 6 - 6
Terminal.Gui/Views/TimeField.cs

@@ -217,7 +217,7 @@ namespace Terminal.Gui {
 		{
 			switch (kb.Key) {
 			case Key.DeleteChar:
-			case Key.ControlD:
+			case Key.D | Key.CtrlMask:
 				if (ReadOnly)
 					return true;
 
@@ -235,28 +235,28 @@ namespace Terminal.Gui {
 
 			// Home, C-A
 			case Key.Home:
-			case Key.ControlA:
+			case Key.A | Key.CtrlMask:
 				CursorPosition = 1;
 				break;
 
 			case Key.CursorLeft:
-			case Key.ControlB:
+			case Key.B | Key.CtrlMask:
 				DecCursorPosition ();
 				break;
 
 			case Key.End:
-			case Key.ControlE: // End
+			case Key.E | Key.CtrlMask: // End
 				CursorPosition = FieldLen;
 				break;
 
 			case Key.CursorRight:
-			case Key.ControlF:
+			case Key.F | Key.CtrlMask:
 				IncCursorPosition ();
 				break;
 
 			default:
 				// Ignore non-numeric characters.
-				if (kb.Key < (Key)((int)'0') || kb.Key > (Key)((int)'9'))
+				if (kb.Key < (Key)((int)Key.D0) || kb.Key > (Key)((int)Key.D9))
 					return false;
 
 				if (ReadOnly)

+ 4 - 4
Terminal.Gui/Windows/FileDialog.cs

@@ -279,16 +279,16 @@ namespace Terminal.Gui {
 		{
 			switch (keyEvent.Key) {
 			case Key.CursorUp:
-			case Key.ControlP:
+			case Key.P | Key.CtrlMask:
 				MoveUp ();
 				return true;
 
 			case Key.CursorDown:
-			case Key.ControlN:
+			case Key.N | Key.CtrlMask:
 				MoveDown ();
 				return true;
 
-			case Key.ControlV:
+			case Key.V | Key.CtrlMask:
 			case Key.PageDown:
 				var n = (selected + Frame.Height);
 				if (n > infos.Count)
@@ -325,7 +325,7 @@ namespace Terminal.Gui {
 				return true;
 
 			case Key.Space:
-			case Key.ControlT:
+			case Key.T | Key.CtrlMask:
 				PerformMultipleSelection ();
 				return true;
 

+ 1 - 1
UICatalog/Scenarios/AllViewsTester.cs

@@ -59,7 +59,7 @@ namespace UICatalog {
 		public override void Setup ()
 		{
 			var statusBar = new StatusBar (new StatusItem [] {
-				new StatusItem(Key.ControlQ, "~^Q~ Quit", () => Quit()),
+				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
 				new StatusItem(Key.F2, "~F2~ Toggle Frame Ruler", () => {
 					ConsoleDriver.Diagnostics ^= ConsoleDriver.DiagnosticFlags.FrameRuler;
 					Top.SetNeedsDisplay ();

+ 1 - 1
UICatalog/Scenarios/ComputedLayout.cs

@@ -28,7 +28,7 @@ namespace UICatalog {
 			Top.Add (menu);
 
 			var statusBar = new StatusBar (new StatusItem [] {
-				new StatusItem(Key.ControlQ, "~^Q~ Quit", () => Quit()),
+				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
 			});
 			Top.Add (statusBar);
 

+ 124 - 104
UICatalog/Scenarios/DynamicMenuBar.cs

@@ -207,74 +207,6 @@ namespace UICatalog {
 			};
 			Add (_frmMenuDetails);
 
-			_btnAdd.Clicked += () => {
-				if (MenuBar == null) {
-					MessageBox.ErrorQuery ("Menu Bar Error", "Must add a MenuBar first!", "Ok");
-					_btnAddMenuBar.SetFocus ();
-					return;
-				}
-
-				var frameDetails = new DynamicMenuBarDetails (null, _currentMenuBarItem != null);
-				var item = frameDetails.EnterMenuItem ();
-				if (item == null) {
-					return;
-				}
-
-				if (!(_currentMenuBarItem is MenuBarItem)) {
-					var parent = _currentMenuBarItem.Parent as MenuBarItem;
-					var idx = parent.GetChildrenIndex (_currentMenuBarItem);
-					_currentMenuBarItem = new MenuBarItem (_currentMenuBarItem.Title, new MenuItem [] { }, _currentMenuBarItem.Parent);
-					_currentMenuBarItem.CheckType = item.checkStyle;
-					parent.Children [idx] = _currentMenuBarItem;
-				} else {
-					MenuItem newMenu = CreateNewMenu (item, _currentMenuBarItem);
-					var menuBarItem = _currentMenuBarItem as MenuBarItem;
-					if (menuBarItem == null) {
-						menuBarItem = new MenuBarItem (_currentMenuBarItem.Title, new MenuItem [] { newMenu }, _currentMenuBarItem.Parent);
-					} else if (menuBarItem.Children == null) {
-						menuBarItem.Children = new MenuItem [] { newMenu };
-					} else {
-						var childrens = menuBarItem.Children;
-						Array.Resize (ref childrens, childrens.Length + 1);
-						childrens [childrens.Length - 1] = newMenu;
-						menuBarItem.Children = childrens;
-					}
-					DataContext.Menus.Add (new DynamicMenuItemList (newMenu.Title, newMenu));
-					_lstMenus.MoveDown ();
-				}
-			};
-
-			_btnRemove.Clicked += () => {
-				var menuItem = DataContext.Menus.Count > 0 ? DataContext.Menus [_lstMenus.SelectedItem].MenuItem : null;
-				if (menuItem != null) {
-					var childrens = ((MenuBarItem)_currentMenuBarItem).Children;
-					childrens [_lstMenus.SelectedItem] = null;
-					int i = 0;
-					foreach (var c in childrens) {
-						if (c != null) {
-							childrens [i] = c;
-							i++;
-						}
-					}
-					Array.Resize (ref childrens, childrens.Length - 1);
-					if (childrens.Length == 0) {
-						if (_currentMenuBarItem.Parent == null) {
-							((MenuBarItem)_currentMenuBarItem).Children = null;
-							_currentMenuBarItem.Action = _frmMenuDetails.CreateAction (_currentEditMenuBarItem, new DynamicMenuItem (_currentMenuBarItem.Title));
-						} else {
-							_currentMenuBarItem = new MenuItem (_currentMenuBarItem.Title, _currentMenuBarItem.Help, _frmMenuDetails.CreateAction (_currentEditMenuBarItem, new DynamicMenuItem (_currentEditMenuBarItem.Title)), null, _currentMenuBarItem.Parent);
-						}
-					} else {
-						((MenuBarItem)_currentMenuBarItem).Children = childrens;
-					}
-					DataContext.Menus.RemoveAt (_lstMenus.SelectedItem);
-					if (_lstMenus.Source.Count > 0 && _lstMenus.SelectedItem > _lstMenus.Source.Count - 1) {
-						_lstMenus.SelectedItem = _lstMenus.Source.Count - 1;
-					}
-					_lstMenus.SetNeedsDisplay ();
-				}
-			};
-
 			_btnMenuBarUp.Clicked += () => {
 				var i = _currentSelectedMenuBar;
 				var menuItem = _menuBar != null && _menuBar.Menus.Length > 0 ? _menuBar.Menus [i] : null;
@@ -369,8 +301,7 @@ namespace UICatalog {
 			Add (_btnCancel);
 
 			_lstMenus.SelectedItemChanged += (e) => {
-				var menuBarItem = DataContext.Menus.Count > 0 ? DataContext.Menus [e.Item].MenuItem : null;
-				SetFrameDetails (menuBarItem);
+				SetFrameDetails ();
 			};
 
 			_btnOk.Clicked += () => {
@@ -389,6 +320,75 @@ namespace UICatalog {
 				}
 			};
 
+			_btnAdd.Clicked += () => {
+				if (MenuBar == null) {
+					MessageBox.ErrorQuery ("Menu Bar Error", "Must add a MenuBar first!", "Ok");
+					_btnAddMenuBar.SetFocus ();
+					return;
+				}
+
+				var frameDetails = new DynamicMenuBarDetails (null, _currentMenuBarItem != null);
+				var item = frameDetails.EnterMenuItem ();
+				if (item == null) {
+					return;
+				}
+
+				if (!(_currentMenuBarItem is MenuBarItem)) {
+					var parent = _currentMenuBarItem.Parent as MenuBarItem;
+					var idx = parent.GetChildrenIndex (_currentMenuBarItem);
+					_currentMenuBarItem = new MenuBarItem (_currentMenuBarItem.Title, new MenuItem [] { }, _currentMenuBarItem.Parent);
+					_currentMenuBarItem.CheckType = item.checkStyle;
+					parent.Children [idx] = _currentMenuBarItem;
+				} else {
+					MenuItem newMenu = CreateNewMenu (item, _currentMenuBarItem);
+					var menuBarItem = _currentMenuBarItem as MenuBarItem;
+					if (menuBarItem == null) {
+						menuBarItem = new MenuBarItem (_currentMenuBarItem.Title, new MenuItem [] { newMenu }, _currentMenuBarItem.Parent);
+					} else if (menuBarItem.Children == null) {
+						menuBarItem.Children = new MenuItem [] { newMenu };
+					} else {
+						var childrens = menuBarItem.Children;
+						Array.Resize (ref childrens, childrens.Length + 1);
+						childrens [childrens.Length - 1] = newMenu;
+						menuBarItem.Children = childrens;
+					}
+					DataContext.Menus.Add (new DynamicMenuItemList (newMenu.Title, newMenu));
+					_lstMenus.MoveDown ();
+				}
+			};
+
+			_btnRemove.Clicked += () => {
+				var menuItem = DataContext.Menus.Count > 0 ? DataContext.Menus [_lstMenus.SelectedItem].MenuItem : null;
+				if (menuItem != null) {
+					var childrens = ((MenuBarItem)_currentMenuBarItem).Children;
+					childrens [_lstMenus.SelectedItem] = null;
+					int i = 0;
+					foreach (var c in childrens) {
+						if (c != null) {
+							childrens [i] = c;
+							i++;
+						}
+					}
+					Array.Resize (ref childrens, childrens.Length - 1);
+					if (childrens.Length == 0) {
+						if (_currentMenuBarItem.Parent == null) {
+							((MenuBarItem)_currentMenuBarItem).Children = null;
+							//_currentMenuBarItem.Action = _frmMenuDetails.CreateAction (_currentEditMenuBarItem, new DynamicMenuItem (_currentMenuBarItem.Title));
+						} else {
+							_currentMenuBarItem = new MenuItem (_currentMenuBarItem.Title, _currentMenuBarItem.Help, _frmMenuDetails.CreateAction (_currentEditMenuBarItem, new DynamicMenuItem (_currentEditMenuBarItem.Title)), null, _currentMenuBarItem.Parent);
+						}
+					} else {
+						((MenuBarItem)_currentMenuBarItem).Children = childrens;
+					}
+					DataContext.Menus.RemoveAt (_lstMenus.SelectedItem);
+					if (_lstMenus.Source.Count > 0 && _lstMenus.SelectedItem > _lstMenus.Source.Count - 1) {
+						_lstMenus.SelectedItem = _lstMenus.Source.Count - 1;
+					}
+					_lstMenus.SetNeedsDisplay ();
+					SetFrameDetails ();
+				}
+			};
+
 			_lstMenus.OpenSelectedItem += (e) => {
 				_currentMenuBarItem = DataContext.Menus [e.Item].MenuItem;
 				if (!(_currentMenuBarItem is MenuBarItem)) {
@@ -503,8 +503,16 @@ namespace UICatalog {
 			var lstMenus = new Binding (this, "Menus", _lstMenus, "Source", listWrapperConverter);
 
 
-			void SetFrameDetails (MenuItem menuItem = null)
+			void SetFrameDetails (MenuItem menuBarItem = null)
 			{
+				MenuItem menuItem;
+
+				if (menuBarItem == null) {
+					menuItem = DataContext.Menus.Count > 0 ? DataContext.Menus [_lstMenus.SelectedItem].MenuItem : null;
+				} else {
+					menuItem = menuBarItem;
+				}
+
 				_currentEditMenuBarItem = menuItem;
 				_frmMenuDetails.EditMenuBarItem (menuItem);
 				var f = _btnOk.CanFocus == _frmMenuDetails.CanFocus;
@@ -555,14 +563,14 @@ namespace UICatalog {
 					newMenu = new MenuItem (item.title, item.help, null, null, parent);
 					newMenu.CheckType = item.checkStyle;
 					newMenu.Action = _frmMenuDetails.CreateAction (newMenu, item);
-					newMenu.ShortCut = newMenu.CreateShortCutFromTag (item.shortCut);
+					newMenu.ShortCut = ShortCutHelper.GetShortCutFromTag (item.shortCut);
 				} else if (item.isTopLevel) {
 					newMenu = new MenuBarItem (item.title, item.help, null);
 					newMenu.Action = _frmMenuDetails.CreateAction (newMenu, item);
 				} else {
 					newMenu = new MenuBarItem (item.title, item.help, null);
 					((MenuBarItem)newMenu).Children [0].Action = _frmMenuDetails.CreateAction (newMenu, item);
-					((MenuBarItem)newMenu).Children [0].ShortCut = newMenu.CreateShortCutFromTag (item.shortCut);
+					((MenuBarItem)newMenu).Children [0].ShortCut = ShortCutHelper.GetShortCutFromTag (item.shortCut);
 				}
 
 				return newMenu;
@@ -600,7 +608,7 @@ namespace UICatalog {
 						DataContext.Menus = new List<DynamicMenuItemList> ();
 					}
 					_currentEditMenuBarItem.Action = _frmMenuDetails.CreateAction (_currentEditMenuBarItem, menuItem);
-					_currentEditMenuBarItem.ShortCut = _currentEditMenuBarItem.CreateShortCutFromTag (menuItem.shortCut);
+					_currentEditMenuBarItem.ShortCut = ShortCutHelper.GetShortCutFromTag (menuItem.shortCut);
 				}
 
 				if (_currentEditMenuBarItem.Parent == null) {
@@ -713,54 +721,53 @@ namespace UICatalog {
 				ReadOnly = true
 			};
 			_txtShortCut.KeyDown += (e) => {
-				var k = GetModifiersKey (e.KeyEvent);
-				if (((k & (Key.CtrlMask | Key.ShiftMask | Key.AltMask)) == 0 && !CheckFlagRange (k, Key.F1, Key.F12))) {
-					_txtShortCut.Text = "";
+				if (!ProcessKey (e.KeyEvent)) {
 					return;
 				}
 
-				GetShortCut (k);
-				e.Handled = true;
+				var k = GetModifiersKey (e.KeyEvent);
+				if (CheckShortCut (k, true)) {
+					e.Handled = true;
+				}
 			};
 
-			bool CheckFlagRange (Key key, Key first, Key last)
+			bool ProcessKey (KeyEvent ev)
 			{
-				for (uint i = (uint)first; i < (uint)last; i++) {
-					if ((key | (Key)i) == key) {
-						return true;
-					}
+				switch (ev.Key) {
+				case Key.CursorUp:
+				case Key.CursorDown:
+				case Key.Tab:
+				case Key.BackTab:
+					return false;
 				}
-				return false;
+
+				return true;
 			}
 
-			void GetShortCut (Key k)
+			bool CheckShortCut (Key k, bool pre)
 			{
 				var m = _menuItem != null ? _menuItem : new MenuItem ();
-				var s = m.GetShortCutTag (k);
-				if (s.Contains ("Unknow")) {
+				if (pre && !ShortCutHelper.PreShortCutValidation (k)) {
 					_txtShortCut.Text = "";
-				} else {
-					_txtShortCut.Text = s;
+					return false;
+				}
+				if (!pre) {
+					if (!ShortCutHelper.PostShortCutValidation (ShortCutHelper.GetShortCutFromTag (_txtShortCut.Text))) {
+						_txtShortCut.Text = "";
+						return false;
+					}
+					return true;
 				}
+				_txtShortCut.Text = ShortCutHelper.GetShortCutTag (k);
+
+				return true;
 			}
 
 			_txtShortCut.KeyUp += (e) => {
 				var k = GetModifiersKey (e.KeyEvent);
-				if ((k & (Key.CtrlMask | Key.ShiftMask | Key.AltMask)) == 0 || ((k | Key.D0) == 0 && !CheckFlagRange (k, Key.F1, Key.F12))) {
-					return;
-				}
-
-				e.Handled = true;
-				var kVal = (Key)e.KeyEvent.KeyValue;
-				if ((kVal & Key.D0) != 0) {
-
-				} else if ((kVal == Key.CtrlMask || kVal == Key.ShiftMask || kVal == Key.AltMask ||
-				       (kVal & (Key.CtrlMask | Key.ShiftMask)) != 0 || (kVal & (Key.CtrlMask | Key.AltMask)) != 0 || (kVal & (Key.ShiftMask | Key.AltMask)) != 0 ||
-				       (kVal & (Key.CtrlMask | Key.ShiftMask | Key.AltMask)) != 0) && !kVal.ToString ().Contains ("Control")) {
-					return;
+				if (CheckShortCut (k, false)) {
+					e.Handled = true;
 				}
-				GetShortCut (k);
-				e.Handled = true;
 			};
 			Add (_txtShortCut);
 
@@ -865,6 +872,7 @@ namespace UICatalog {
 			Height = Dim.Fill () - 1;
 			_dialog.Add (this);
 			_txtTitle.SetFocus ();
+			_txtTitle.CursorPosition = _txtTitle.Text.Length;
 			Application.Run (_dialog);
 
 			if (valid) {
@@ -884,6 +892,7 @@ namespace UICatalog {
 			if (menuItem == null) {
 				hasParent = false;
 				CanFocus = false;
+				CleanEditMenuBarItem ();
 				return;
 			} else {
 				hasParent = menuItem.Parent != null;
@@ -902,6 +911,17 @@ namespace UICatalog {
 			_txtShortCut.CanFocus = !_ckbIsTopLevel.Checked && !_ckbSubMenu.Checked;
 		}
 
+		void CleanEditMenuBarItem ()
+		{
+			_txtTitle.Text = "";
+			_txtHelp.Text = "";
+			_txtAction.Text = "";
+			_ckbIsTopLevel.Checked = false;
+			_ckbSubMenu.Checked = false;
+			_rbChkStyle.SelectedItem = (int)MenuItemCheckStyle.NoCheck;
+			_txtShortCut.Text = "";
+		}
+
 		ustring GetTargetAction (Action action)
 		{
 			var me = action.Target;

+ 1 - 1
UICatalog/Scenarios/Editor.cs

@@ -41,7 +41,7 @@ namespace UICatalog {
 			var statusBar = new StatusBar (new StatusItem [] {
 				new StatusItem(Key.F2, "~F2~ Open", () => Open()),
 				new StatusItem(Key.F3, "~F3~ Save", () => Save()),
-				new StatusItem(Key.ControlQ, "~^Q~ Quit", () => Quit()),
+				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
 			});
 			Top.Add (statusBar);
 

+ 1 - 1
UICatalog/Scenarios/HexEditor.cs

@@ -42,7 +42,7 @@ namespace UICatalog {
 				//new StatusItem(Key.Enter, "~ENTER~ ApplyEdits", () => { _hexView.ApplyEdits(); }),
 				new StatusItem(Key.F2, "~F2~ Open", () => Open()),
 				new StatusItem(Key.F3, "~F3~ Save", () => Save()),
-				new StatusItem(Key.ControlQ, "~^Q~ Quit", () => Quit()),
+				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
 			});
 			Top.Add (statusBar);
 

+ 1 - 1
UICatalog/Scenarios/Keys.cs

@@ -101,7 +101,7 @@ namespace UICatalog {
 				Y = Pos.Top (editLabel) + 4,
 			};
 			Win.Add (keyLogLabel);
-			var fakeKeyPress = new KeyEvent (Key.ControlA, new KeyModifiers () {
+			var fakeKeyPress = new KeyEvent (Key.CtrlMask | Key.A, new KeyModifiers () {
 				Alt = true,
 				Ctrl = true,
 				Shift = true

+ 1 - 1
UICatalog/Scenarios/Unicode.cs

@@ -37,7 +37,7 @@ namespace UICatalog {
 			Top.Add (menu);
 
 			var statusBar = new StatusBar (new StatusItem [] {
-				new StatusItem (Key.ControlQ, "~^Q~ Выход", () => Application.RequestStop()),
+				new StatusItem (Key.CtrlMask | Key.Q, "~^Q~ Выход", () => Application.RequestStop()),
 				new StatusItem (Key.Unknown, "~F2~ Создать", null),
 				new StatusItem(Key.Unknown, "~F3~ Со_хранить", null),
 			});

+ 3 - 3
UICatalog/UICatalog.cs

@@ -157,14 +157,14 @@ namespace UICatalog {
 
 			_menu = new MenuBar (new MenuBarItem [] {
 				new MenuBarItem ("_File", new MenuItem [] {
-					new MenuItem ("_Quit", "", () => Application.RequestStop(), null, null, Key.ControlQ)
+					new MenuItem ("_Quit", "", () => Application.RequestStop(), null, null, Key.Q | Key.CtrlMask)
 				}),
 				new MenuBarItem ("_Color Scheme", CreateColorSchemeMenuItems()),
 				new MenuBarItem ("Diag_ostics", CreateDiagnosticMenuItems()),
 				new MenuBarItem ("_Help", new MenuItem [] {
 					new MenuItem ("_gui.cs API Overview", "", () => OpenUrl ("https://migueldeicaza.github.io/gui.cs/articles/overview.html"), null, null, Key.F1),
 					new MenuItem ("gui.cs _README", "", () => OpenUrl ("https://github.com/migueldeicaza/gui.cs"), null, null, Key.F2),
-					new MenuItem ("_About...", "About this app", () =>  MessageBox.Query ("About UI Catalog", aboutMessage.ToString(), "_Ok"), null, null, Key.ControlA),
+					new MenuItem ("_About...", "About this app", () =>  MessageBox.Query ("About UI Catalog", aboutMessage.ToString(), "_Ok"), null, null, Key.CtrlMask | Key.A),
 				})
 			});
 
@@ -229,7 +229,7 @@ namespace UICatalog {
 				_capslock,
 				_numlock,
 				_scrolllock,
-				new StatusItem(Key.ControlQ, "~CTRL-Q~ Quit", () => {
+				new StatusItem(Key.Q | Key.CtrlMask, "~CTRL-Q~ Quit", () => {
 					if (_runningScenario is null){
 						// This causes GetScenarioToRun to return null
 						_runningScenario = null;

+ 2 - 2
UnitTests/ApplicationTests.cs

@@ -168,7 +168,7 @@ namespace Terminal.Gui {
 			Console.MockKeyPresses.Push (new ConsoleKeyInfo ('q', ConsoleKey.Q, shift: false, alt: false, control: true));
 			foreach (var c in input.Reverse ()) {
 				if (char.IsLetter (c)) {
-					Console.MockKeyPresses.Push (new ConsoleKeyInfo (char.ToLower (c), (ConsoleKey)char.ToUpper (c), shift: char.IsUpper (c), alt: false, control: false));
+					Console.MockKeyPresses.Push (new ConsoleKeyInfo (c, (ConsoleKey)char.ToUpper (c), shift: char.IsUpper (c), alt: false, control: false));
 				} else {
 					Console.MockKeyPresses.Push (new ConsoleKeyInfo (c, (ConsoleKey)c, shift: false, alt: false, control: false));
 				}
@@ -188,7 +188,7 @@ namespace Terminal.Gui {
 			int keyUps = 0;
 			var output = string.Empty;
 			Application.Top.KeyUp += (View.KeyEventEventArgs args) => {
-				if (args.KeyEvent.Key != Key.ControlQ) {
+				if (args.KeyEvent.Key != (Key.CtrlMask | Key.Q)) {
 					output += (char)args.KeyEvent.KeyValue;
 				}
 				keyUps++;

+ 1 - 1
UnitTests/ScenarioTests.cs

@@ -119,7 +119,7 @@ namespace Terminal.Gui {
 			var token = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (ms), abortCallback);
 
 			Application.Top.KeyPress += (View.KeyEventEventArgs args) => {
-				Assert.Equal (Key.ControlQ, args.KeyEvent.Key);
+				Assert.Equal (Key.CtrlMask | Key.Q, args.KeyEvent.Key);
 			};
 
 			var scenario = (Scenario)Activator.CreateInstance (scenarioClass);