Ver código fonte

Merge pull request #946 from BDisp/lazy-shortcut-menu

Fixes #881, #928. Allowing more key combinations.
Charlie Kindel 4 anos atrás
pai
commit
c1d3fcfb0c

+ 28 - 32
Example/demo.cs

@@ -105,7 +105,7 @@ static class Demo {
 
 	static void ShowTextAlignments ()
 	{
-		var container = new Window ($"Show Text Alignments") {
+		var container = new Window ("Show Text Alignments - Press Esc to return") {
 			X = 0,
 			Y = 0,
 			Width = Dim.Fill (),
@@ -244,12 +244,12 @@ static class Demo {
 
 		var menu = new MenuBar (new MenuBarItem [] {
 			new MenuBarItem ("_File", new MenuItem [] {
-				new MenuItem ("_Close", "", () => { if (Quit ()) { running = MainApp; Application.RequestStop (); } }),
+				new MenuItem ("_Close", "", () => { if (Quit ()) { running = MainApp; Application.RequestStop (); } }, null, null, Key.AltMask | Key.F4),
 			}),
 			new MenuBarItem ("_Edit", new MenuItem [] {
-				new MenuItem ("_Copy", "", null),
-				new MenuItem ("C_ut", "", null),
-				new MenuItem ("_Paste", "", null)
+				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);
@@ -309,13 +309,12 @@ static class Demo {
 			MessageBox.Query (50, 7, "Selected File", d.FilePaths.Count > 0 ? string.Join (", ", d.FilePaths) : d.FilePath, "Ok");
 	}
 
-	public static void ShowHex (Toplevel top)
+	public static void ShowHex ()
 	{
-		var tframe = top.Frame;
-		var ntop = new Toplevel (tframe);
+		var ntop = Application.Top;
 		var menu = new MenuBar (new MenuBarItem [] {
 			new MenuBarItem ("_File", new MenuItem [] {
-				new MenuItem ("_Close", "", () => {Application.RequestStop ();}),
+				new MenuItem ("_Close", "", () => { running = MainApp; Application.RequestStop (); }, null, null, Key.AltMask | Key.F4),
 			}),
 		});
 		ntop.Add (menu);
@@ -338,7 +337,6 @@ static class Demo {
 		};
 		win.Add (hex);
 		Application.Run (ntop);
-
 	}
 
 	public class MenuItemDetails : MenuItem {
@@ -382,7 +380,7 @@ static class Demo {
 
 	static void Copy ()
 	{
-		TextField textField = menu.LastFocused as TextField;
+		TextField textField = menu.LastFocused as TextField ?? Application.Top.MostFocused as TextField;
 		if (textField != null && textField.SelectedLength != 0) {
 			textField.Copy ();
 		}
@@ -390,7 +388,7 @@ static class Demo {
 
 	static void Cut ()
 	{
-		TextField textField = menu.LastFocused as TextField;
+		TextField textField = menu.LastFocused as TextField ?? Application.Top.MostFocused as TextField;
 		if (textField != null && textField.SelectedLength != 0) {
 			textField.Cut ();
 		}
@@ -398,10 +396,8 @@ static class Demo {
 
 	static void Paste ()
 	{
-		TextField textField = menu.LastFocused as TextField;
-		if (textField != null) {
-			textField.Paste ();
-		}
+		TextField textField = menu.LastFocused as TextField ?? Application.Top.MostFocused as TextField;
+		textField?.Paste ();
 	}
 
 	static void Help ()
@@ -601,31 +597,31 @@ static class Demo {
 
 		menu = new MenuBar (new MenuBarItem [] {
 			new MenuBarItem ("_File", new MenuItem [] {
-				new MenuItem ("Text _Editor Demo", "", () => { running = Editor; Application.RequestStop (); }),
-				new MenuItem ("_New", "Creates new file", NewFile),
-				new MenuItem ("_Open", "", Open),
-				new MenuItem ("_Hex", "", () => ShowHex (top)),
-				new MenuItem ("_Close", "", () => Close ()),
+				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; } })
+				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),
-				new MenuItem ("C_ut", "", Cut),
-				new MenuItem ("_Paste", "", Paste),
+				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]
 			}),
 			new MenuBarItem ("_List Demos", new MenuItem [] {
-				new MenuItem ("Select _Multiple Items", "", () => ListSelectionDemo (true)),
-				new MenuItem ("Select _Single Item", "", () => ListSelectionDemo (false)),
-				new MenuItem ("Search Single Item", "", ComboBoxDemo)
+				new MenuItem ("Select _Multiple Items", "", () => ListSelectionDemo (true), null, null, Key.AltMask + 0.ToString () [0]),
+				new MenuItem ("Select _Single Item", "", () => ListSelectionDemo (false), null, null, Key.AltMask + 1.ToString () [0]),
+				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 ()),
-				new MenuItem ("_OnKeyDown/Press/Up", "", () => OnKeyDownPressUpDemo ())
+				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 [] {
@@ -666,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);
@@ -693,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

+ 115 - 19
Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs

@@ -195,6 +195,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;
 			}
 		}
@@ -452,11 +464,11 @@ namespace Terminal.Gui {
 			if (keyModifiers == null)
 				keyModifiers = new KeyModifiers ();
 
-			if (!keyModifiers.Shift && key.HasFlag (Key.ShiftMask))
+			if (!keyModifiers.Shift && (key & Key.ShiftMask) != 0)
 				keyModifiers.Shift = true;
-			if (!keyModifiers.Alt && key.HasFlag (Key.AltMask))
+			if (!keyModifiers.Alt && (key & Key.AltMask) != 0)
 				keyModifiers.Alt = true;
-			if (!keyModifiers.Ctrl && key.HasFlag (Key.CtrlMask))
+			if (!keyModifiers.Ctrl && (key & Key.CtrlMask) != 0)
 				keyModifiers.Ctrl = true;
 
 			return keyModifiers;
@@ -470,7 +482,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,8 +497,26 @@ namespace Terminal.Gui {
 					mouseHandler (ToDriverMouse (ev));
 					return;
 				}
-				keyHandler (new KeyEvent (MapCursesKey (wch), keyModifiers));
-				keyUpHandler (new KeyEvent (MapCursesKey (wch), keyModifiers));
+				k = MapCursesKey (wch);
+				if (wch >= 277 && wch <= 288) { // Shift+(F1 - F12)
+					wch -= 12;
+					k = Key.ShiftMask | MapCursesKey (wch);
+				} else if (wch >= 289 && wch <= 300) { // Ctrl+(F1 - F12)
+					wch -= 24;
+					k = Key.CtrlMask | MapCursesKey (wch);
+				} else if (wch >= 301 && wch <= 312) { // Ctrl+Shift+(F1 - F12)
+					wch -= 36;
+					k = Key.CtrlMask | Key.ShiftMask | MapCursesKey (wch);
+				} 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)));
+				keyUpHandler (new KeyEvent (k, MapKeyModifiers (k)));
 				return;
 			}
 
@@ -498,7 +528,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;
@@ -507,23 +536,80 @@ 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 >= '1' && wch <= '9') {
-						k = (Key)((int)Key.F1 + (wch2 - '0' - 1));
-						key = new KeyEvent (k, MapKeyModifiers (k));
-					} else if (wch2 == '0') {
-						k = Key.F10;
-						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 == Curses.KEY_CODE_SEQ) {
+						int [] c = null;
+						while (code == 0) {
+							code = Curses.get_wch (out wch2);
+							if (wch2 > 0) {
+								Array.Resize (ref c, c == null ? 1 : c.Length + 1);
+								c [c.Length - 1] = wch2;
+							}
+						}
+						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 && 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 && 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 && 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 && 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 && 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);
+						}
 					} else {
-						k = Key.AltMask | (Key)wch2;
-						key = new KeyEvent (k, MapKeyModifiers (k));
+						// 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 {
+							k = (Key)((uint)(Key.AltMask | Key.CtrlMask) + wch2);
+						}
 					}
+					key = new KeyEvent (k, MapKeyModifiers (k));
+					keyDownHandler (key);
 					keyHandler (key);
 				} else {
 					k = Key.Esc;
@@ -534,7 +620,17 @@ namespace Terminal.Gui {
 				keyDownHandler (new KeyEvent (k, MapKeyModifiers (k)));
 				keyHandler (new KeyEvent (k, MapKeyModifiers (k)));
 			} else {
+				// Unfortunately there are no way to differentiate Ctrl+alfa and Ctrl+Shift+alfa.
 				k = (Key)wch;
+				if (wch == 0) {
+					k = Key.CtrlMask | Key.Space;
+				} 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)));
 				keyHandler (new KeyEvent (k, MapKeyModifiers (k)));
 			}

+ 2 - 0
Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs

@@ -271,7 +271,9 @@ namespace Unix.Terminal {
 		static public int mouseinterval (int interval) => methods.mouseinterval (interval);
 	}
 
+#pragma warning disable RCS1102 // Make class static.
 	internal class Delegates {
+#pragma warning restore RCS1102 // Make class static.
 		public delegate IntPtr initscr ();
 		public delegate int endwin ();
 		public delegate bool isendwin ();

+ 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;

+ 38 - 26
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:
@@ -1118,30 +1118,42 @@ 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);
-				if (keyInfo.Modifiers == ConsoleModifiers.Alt)
-					return (Key)(((uint)Key.AltMask) | ((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.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);
 				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.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.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);
 			}
 			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);
 			}
 			if (keyInfo.KeyChar != 0) {
 				return (Key)((uint)keyInfo.KeyChar);
@@ -1153,14 +1165,14 @@ namespace Terminal.Gui {
 		private Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key)
 		{
 			Key keyMod = new Key ();
-			if (keyInfo.Modifiers.HasFlag (ConsoleModifiers.Shift))
+			if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0)
 				keyMod = Key.ShiftMask;
-			if (keyInfo.Modifiers.HasFlag (ConsoleModifiers.Control))
+			if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0)
 				keyMod |= Key.CtrlMask;
-			if (keyInfo.Modifiers.HasFlag (ConsoleModifiers.Alt))
+			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 - 64
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>
-		ControlSpace = 0,
+		Null = 0,
 
 		/// <summary>
-		/// The key code for the user pressing Control-A
+		/// The key code for the user pressing the return key.
 		/// </summary>
-		ControlA = 1,
+		Enter = '\n',
+
 		/// <summary>
-		/// The key code for the user pressing Control-B
+		/// The key code for the user pressing the escape key
 		/// </summary>
-		ControlB,
+		Esc = 27,
+
 		/// <summary>
-		/// The key code for the user pressing Control-C
+		/// The key code for the user pressing the space bar
 		/// </summary>
-		ControlC,
+		Space = 32,
+
 		/// <summary>
-		/// The key code for the user pressing Control-D
+		/// Digit 0.
 		/// </summary>
-		ControlD,
+		D0 = 48,
 		/// <summary>
-		/// The key code for the user pressing Control-E
+		/// Digit 1.
 		/// </summary>
-		ControlE,
+		D1,
 		/// <summary>
-		/// The key code for the user pressing Control-F
+		/// Digit 2.
 		/// </summary>
-		ControlF,
+		D2,
 		/// <summary>
-		/// The key code for the user pressing Control-G
+		/// Digit 3.
 		/// </summary>
-		ControlG,
+		D3,
 		/// <summary>
-		/// The key code for the user pressing Control-H
+		/// Digit 4.
 		/// </summary>
-		ControlH,
+		D4,
 		/// <summary>
-		/// The key code for the user pressing Control-I (same as the tab key).
+		/// Digit 5.
 		/// </summary>
-		ControlI,
+		D5,
 		/// <summary>
-		/// The key code for the user pressing Control-J
+		/// Digit 6.
 		/// </summary>
-		ControlJ,
+		D6,
 		/// <summary>
-		/// The key code for the user pressing Control-K
+		/// Digit 7.
 		/// </summary>
-		ControlK,
+		D7,
 		/// <summary>
-		/// The key code for the user pressing Control-L
+		/// Digit 8.
 		/// </summary>
-		ControlL,
+		D8,
 		/// <summary>
-		/// The key code for the user pressing Control-M
+		/// Digit 9.
 		/// </summary>
-		ControlM,
+		D9,
+
 		/// <summary>
-		/// The key code for the user pressing Control-N (same as the return key).
+		/// The key code for the user pressing Shift-A
 		/// </summary>
-		ControlN,
+		A = 65,
 		/// <summary>
-		/// The key code for the user pressing Control-O
+		/// The key code for the user pressing Shift-B
 		/// </summary>
-		ControlO,
+		B,
 		/// <summary>
-		/// The key code for the user pressing Control-P
+		/// The key code for the user pressing Shift-C
 		/// </summary>
-		ControlP,
+		C,
 		/// <summary>
-		/// The key code for the user pressing Control-Q
+		/// The key code for the user pressing Shift-D
 		/// </summary>
-		ControlQ,
+		D,
 		/// <summary>
-		/// The key code for the user pressing Control-R
+		/// The key code for the user pressing Shift-E
 		/// </summary>
-		ControlR,
+		E,
 		/// <summary>
-		/// The key code for the user pressing Control-S
+		/// The key code for the user pressing Shift-F
 		/// </summary>
-		ControlS,
+		F,
 		/// <summary>
-		/// The key code for the user pressing Control-T
+		/// The key code for the user pressing Shift-G
 		/// </summary>
-		ControlT,
+		G,
 		/// <summary>
-		/// The key code for the user pressing Control-U
+		/// The key code for the user pressing Shift-H
 		/// </summary>
-		ControlU,
+		H,
 		/// <summary>
-		/// The key code for the user pressing Control-V
+		/// The key code for the user pressing Shift-I
 		/// </summary>
-		ControlV,
+		I,
 		/// <summary>
-		/// The key code for the user pressing Control-W
+		/// The key code for the user pressing Shift-J
 		/// </summary>
-		ControlW,
+		J,
 		/// <summary>
-		/// The key code for the user pressing Control-X
+		/// The key code for the user pressing Shift-K
 		/// </summary>
-		ControlX,
+		K,
 		/// <summary>
-		/// The key code for the user pressing Control-Y
+		/// The key code for the user pressing Shift-L
 		/// </summary>
-		ControlY,
+		L,
 		/// <summary>
-		/// The key code for the user pressing Control-Z
+		/// The key code for the user pressing Shift-M
 		/// </summary>
-		ControlZ,
-
+		M,
 		/// <summary>
-		/// The key code for the user pressing the escape key
+		/// The key code for the user pressing Shift-N
 		/// </summary>
-		Esc = 27,
-
+		N,
 		/// <summary>
-		/// The key code for the user pressing the return key.
+		/// The key code for the user pressing Shift-O
 		/// </summary>
-		Enter = '\n',
-
+		O,
 		/// <summary>
-		/// The key code for the user pressing the space bar
+		/// The key code for the user pressing Shift-P
 		/// </summary>
-		Space = 32,
+		P,
+		/// <summary>
+		/// The key code for the user pressing Shift-Q
+		/// </summary>
+		Q,
+		/// <summary>
+		/// The key code for the user pressing Shift-R
+		/// </summary>
+		R,
+		/// <summary>
+		/// The key code for the user pressing Shift-S
+		/// </summary>
+		S,
+		/// <summary>
+		/// The key code for the user pressing Shift-T
+		/// </summary>
+		T,
+		/// <summary>
+		/// The key code for the user pressing Shift-U
+		/// </summary>
+		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,6 +363,7 @@ namespace Terminal.Gui {
 		/// Shift-tab key (backwards tab key).
 		/// </summary>
 		BackTab,
+
 		/// <summary>
 		/// A key with an unknown mapping was raised.
 		/// </summary>

+ 265 - 0
Terminal.Gui/Core/ShortcutHelper.cs

@@ -0,0 +1,265 @@
+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>
+		/// The action to run if the <see cref="Shortcut"/> is defined.
+		/// </summary>
+		public virtual Action ShortcutAction { get; set; }
+
+		/// <summary>
+		/// Gets the key with all the keys modifiers, especially the shift key that sometimes have to be injected later.
+		/// </summary>
+		/// <param name="kb">The <see cref="KeyEvent"/> to check.</param>
+		/// <returns>The <see cref="KeyEvent.Key"/> with all the keys modifiers.</returns>
+		public static Key GetModifiersKey (KeyEvent kb)
+		{
+			var key = kb.Key;
+			if (kb.IsAlt && (key & Key.AltMask) == 0) {
+				key |= Key.AltMask;
+			}
+			if (kb.IsCtrl && (key & Key.CtrlMask) == 0) {
+				key |= Key.CtrlMask;
+			}
+			if (kb.IsShift && (key & Key.ShiftMask) == 0) {
+				key |= Key.ShiftMask;
+			}
+
+			return key;
+		}
+
+		/// <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;
+		}
+
+		/// <summary>
+		/// Allows a view to run a <see cref="View.ShortcutAction"/> if defined.
+		/// </summary>
+		/// <param name="kb">The <see cref="KeyEvent"/></param>
+		/// <param name="view">The <see cref="View"/></param>
+		/// <returns><c>true</c> if defined <c>false</c>otherwise.</returns>
+		public static bool FindAndOpenByShortcut (KeyEvent kb, View view = null)
+		{
+			if (view == null) {
+				return false;			}
+
+			var key = kb.KeyValue;
+			var keys = GetModifiersKey (kb);
+			key |= (int)keys;
+			foreach (var v in view.Subviews) {
+				if (v.Shortcut != Key.Null && v.Shortcut == (Key)key) {
+					var action = v.ShortcutAction;
+					if (action != null) {
+						Application.MainLoop.AddIdle (() => {
+							action ();
+							return false;
+						});
+					}
+					return true;
+				}
+				if (FindAndOpenByShortcut (kb, v)) {
+					return true;
+				}
+			}
+
+			return false;
+		}
+	}
+}

+ 17 - 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,13 +206,26 @@ namespace Terminal.Gui {
 				}
 				return true;
 
-			case Key.ControlL:
+			case Key.L | Key.CtrlMask:
 				Application.Refresh ();
 				return true;
 			}
 			return false;
 		}
 
+		///<inheritdoc/>
+		public override bool ProcessColdKey (KeyEvent keyEvent)
+		{
+			if (base.ProcessColdKey (keyEvent)) {
+				return true;
+			}
+
+			if (ShortcutHelper.FindAndOpenByShortcut(keyEvent, this)) {
+				return true;
+			}
+			return false;
+		}
+
 		View GetDeepestFocusedSubview (View view)
 		{
 			if (view == null) {

+ 30 - 0
Terminal.Gui/Core/View.cs

@@ -125,6 +125,8 @@ namespace Terminal.Gui {
 
 		TextFormatter textFormatter;
 
+		ShortcutHelper shortcutHelper;
+
 		/// <summary>
 		/// Event fired when a subview is being added to this view.
 		/// </summary>
@@ -170,6 +172,28 @@ namespace Terminal.Gui {
 		/// </summary>
 		public Rune HotKeySpecifier { get => textFormatter.HotKeySpecifier; set => textFormatter.HotKeySpecifier = value; }
 
+		/// <summary>
+		/// This is the global setting that can be used as a global shortcut to invoke an action if provided.
+		/// </summary>
+		public Key Shortcut {
+			get => shortcutHelper.Shortcut;
+			set {
+				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.
+		/// </summary>
+		public ustring ShortcutTag => ShortcutHelper.GetShortcutTag (shortcutHelper.Shortcut);
+
+		/// <summary>
+		/// The action to run if the <see cref="Shortcut"/> is defined.
+		/// </summary>
+		public virtual Action ShortcutAction { get; set; }
+
 		/// <summary>
 		/// Gets or sets arbitrary data for the view.
 		/// </summary>
@@ -550,6 +574,8 @@ namespace Terminal.Gui {
 			textFormatter = new TextFormatter ();
 			this.Text = ustring.Empty;
 
+			shortcutHelper = new ShortcutHelper ();
+
 			this.Frame = frame;
 			LayoutStyle = LayoutStyle.Absolute;
 		}
@@ -612,6 +638,8 @@ namespace Terminal.Gui {
 		{
 			textFormatter = new TextFormatter ();
 			this.Text = text;
+
+			shortcutHelper = new ShortcutHelper ();
 		}
 
 		/// <summary>
@@ -633,6 +661,8 @@ namespace Terminal.Gui {
 			textFormatter = new TextFormatter ();
 			this.Text = text;
 
+			shortcutHelper = new ShortcutHelper ();
+
 			CanFocus = false;
 			TabIndex = -1;
 			TabStop = false;

+ 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) {

+ 136 - 34
Terminal.Gui/Views/Menu.cs

@@ -5,7 +5,7 @@
 //   Miguel de Icaza ([email protected])
 //
 // TODO:
-//   Add accelerator support, but should also support chords (ShortCut in MenuItem)
+//   Add accelerator support, but should also support chords (Shortcut in MenuItem)
 //   Allow menus inside menus
 
 using System;
@@ -40,14 +40,21 @@ namespace Terminal.Gui {
 	/// A <see cref="MenuItem"/> has a title, an associated help text, and an action to execute on activation.
 	/// </summary>
 	public class MenuItem {
+		ustring title;
+
+		ShortcutHelper shortcutHelper;
 
 		/// <summary>
 		/// Initializes a new instance of <see cref="MenuItem"/>
 		/// </summary>
-		public MenuItem ()
+		public MenuItem (Key shortcut = Key.Null)
 		{
 			Title = "";
 			Help = "";
+			shortcutHelper = new ShortcutHelper ();
+			if (shortcut != Key.Null) {
+				shortcutHelper.Shortcut = shortcut;
+			}
 		}
 
 		/// <summary>
@@ -57,40 +64,58 @@ namespace Terminal.Gui {
 		/// <param name="help">Help text to display.</param>
 		/// <param name="action">Action to invoke when the menu item is activated.</param>
 		/// <param name="canExecute">Function to determine if the action can currently be executed.</param>
-		/// <param name="parent">The parent of this menu item.</param>
-		public MenuItem (ustring title, ustring help, Action action, Func<bool> canExecute = null, MenuItem parent = null)
+		/// <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.Null)
 		{
 			Title = title ?? "";
 			Help = help ?? "";
 			Action = action;
 			CanExecute = canExecute;
-			bool nextIsHot = false;
-			foreach (var x in Title) {
-				if (x == '_')
-					nextIsHot = true;
-				else {
-					if (nextIsHot) {
-						HotKey = Char.ToUpper ((char)x);
-						break;
-					}
-					nextIsHot = false;
-				}
-			}
 			Parent = parent;
+			shortcutHelper = new ShortcutHelper ();
+			if (shortcut != Key.Null) {
+				shortcutHelper.Shortcut = shortcut;
+			}
 		}
 
 		/// <summary>
 		/// The HotKey is used when the menu is active, the shortcut can be triggered when the menu is not active.
 		/// For example HotKey would be "N" when the File Menu is open (assuming there is a "_New" entry
-		/// if the ShortCut is set to "Control-N", this would be a global hotkey that would trigger as well
+		/// if the Shortcut is set to "Control-N", this would be a global hotkey that would trigger as well
 		/// </summary>
 		public Rune HotKey;
 
 		/// <summary>
-		/// Gets or sets the title. 
+		/// 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 => shortcutHelper.Shortcut;
+			set {
+				if (shortcutHelper.Shortcut != value && (ShortcutHelper.PostShortcutValidation (value) || value == Key.Null)) {
+					shortcutHelper.Shortcut = value;
+				}
+			}
+		}
+
+		/// <summary>
+		/// The keystroke combination used in the <see cref="ShortcutHelper.ShortcutTag"/> as string.
+		/// </summary>
+		public ustring ShortcutTag => ShortcutHelper.GetShortcutTag (shortcutHelper.Shortcut);
+
+		/// <summary>
+		/// Gets or sets the title.
 		/// </summary>
 		/// <value>The title.</value>
-		public ustring Title { get; set; }
+		public ustring Title {
+			get { return title; }
+			set {
+				if (title != value) {
+					title = value;
+					GetHotKey ();
+				}
+			}
+		}
 
 		/// <summary>
 		/// Gets or sets the help text for the menu item.
@@ -119,7 +144,8 @@ namespace Terminal.Gui {
 		}
 
 		internal int Width => Title.RuneCount + Help.RuneCount + 1 + 2 +
-			(Checked || CheckType.HasFlag (MenuItemCheckStyle.Checked) || CheckType.HasFlag (MenuItemCheckStyle.Radio) ? 2 : 0);
+			(Checked || CheckType.HasFlag (MenuItemCheckStyle.Checked) || CheckType.HasFlag (MenuItemCheckStyle.Radio) ? 2 : 0) +
+			(ShortcutTag.RuneCount > 0 ? ShortcutTag.RuneCount + 2 : 0);
 
 		/// <summary>
 		/// Sets or gets whether the <see cref="MenuItem"/> shows a check indicator or not. See <see cref="MenuItemCheckStyle"/>.
@@ -157,6 +183,23 @@ namespace Terminal.Gui {
 		{
 			return IsFromSubMenu;
 		}
+
+		void GetHotKey ()
+		{
+			bool nextIsHot = false;
+			foreach (var x in title) {
+				if (x == '_') {
+					nextIsHot = true;
+				} else {
+					if (nextIsHot) {
+						HotKey = Char.ToUpper ((char)x);
+						break;
+					}
+					nextIsHot = false;
+					HotKey = default;
+				}
+			}
+		}
 	}
 
 	/// <summary>
@@ -307,7 +350,7 @@ namespace Terminal.Gui {
 
 		internal int TitleLength => GetMenuBarItemLength (Title);
 
-		internal bool IsTopLevel { get => Parent == null && (Children == null || Children.Length == 0); }
+		internal bool IsTopLevel { get => Parent == null && (Children == null || Children.Length == 0) && Action != null; }
 
 	}
 
@@ -338,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;
@@ -416,9 +459,16 @@ namespace Terminal.Gui {
 					       i == current ? ColorScheme.Focus : ColorScheme.Normal);
 
 				// The help string
-				var l = item.Help.RuneCount;
+				var l = item.ShortcutTag.RuneCount == 0 ? item.Help.RuneCount : item.Help.RuneCount + item.ShortcutTag.RuneCount + 2;
 				Move (Frame.Width - l - 2, 1 + i);
 				Driver.AddStr (item.Help);
+
+				// The shortcut tag string
+				if (!item.ShortcutTag.IsEmpty) {
+					l = item.ShortcutTag.RuneCount;
+					Move (Frame.Width - l - 2, 1 + i);
+					Driver.AddStr (item.ShortcutTag);
+				}
 			}
 			PositionCursor ();
 		}
@@ -704,6 +754,19 @@ namespace Terminal.Gui {
 		/// </summary>
 		public bool UseKeysUpDownAsKeysLeftRight { get; set; } = true;
 
+		static ustring shortcutDelimiter = "+";
+		/// <summary>
+		/// Used for change the shortcut delimiter separator.
+		/// </summary>
+		public static ustring ShortcutDelimiter {
+			get => shortcutDelimiter;
+			set {
+				if (shortcutDelimiter != value) {
+					shortcutDelimiter = value == ustring.Empty ? " " : value;
+				}
+			}
+		}
+
 		/// <summary>
 		/// Initializes a new instance of the <see cref="MenuBar"/>.
 		/// </summary>
@@ -828,8 +891,8 @@ namespace Terminal.Gui {
 					hotColor = ColorScheme.Normal;
 					normalColor = ColorScheme.Normal;
 				}
-				DrawHotString ($" {menu.Title}  ", hotColor, normalColor);
-				pos += 1 + menu.TitleLength + 2;
+				DrawHotString (menu.Help.IsEmpty ? $" {menu.Title}  " : $" {menu.Title}  {menu.Help}  ", hotColor, normalColor);
+				pos += 1 + menu.TitleLength + (menu.Help.Length > 0 ? menu.Help.Length + 2 : 0) + 2;
 			}
 			PositionCursor ();
 		}
@@ -843,14 +906,14 @@ namespace Terminal.Gui {
 					pos++;
 					if (IsMenuOpen)
 						Move (pos + 1, 0);
-					else
+					else {
 						Move (pos + 1, 0);
+					}
 					return;
+				} else if (IsMenuOpen) {
+					pos += 1 + Menus [i].TitleLength + (Menus [i].Help.Length > 0 ? Menus [i].Help.Length + 2 : 0) + 2;
 				} else {
-					if (IsMenuOpen)
-						pos += 1 + Menus [i].TitleLength + 2;
-					else
-						pos += 2 + Menus [i].TitleLength + 1;
+					pos += 2 + Menus [i].TitleLength + (Menus [i].Help.Length > 0 ? Menus [i].Help.Length + 2 : 0) + 1;
 				}
 			}
 			//Move (0, 0);
@@ -923,7 +986,7 @@ namespace Terminal.Gui {
 				}
 
 				for (int i = 0; i < index; i++)
-					pos += Menus [i].Title.RuneCount + 2;
+					pos += Menus [i].Title.RuneCount + (Menus [i].Help.RuneCount > 0 ? Menus [i].Help.RuneCount + 2 : 0) + 2;
 				openMenu = new Menu (this, pos, 1, Menus [index]);
 				openCurrentMenu = openMenu;
 				openCurrentMenu.previousSubFocused = openMenu;
@@ -1258,6 +1321,38 @@ namespace Terminal.Gui {
 			return false;
 		}
 
+		internal bool FindAndOpenMenuByShortcut (KeyEvent kb, MenuItem [] children = null)
+		{
+			if (children == null) {
+				children = Menus;
+			}
+
+			var key = kb.KeyValue;
+			var keys = ShortcutHelper.GetModifiersKey (kb);
+			key |= (int)keys;
+			for (int i = 0; i < children.Length; i++) {
+				var mi = children [i];
+				if (mi == null) {
+					continue;
+				}
+				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 (() => {
+							action ();
+							return false;
+						});
+					}
+					return true;
+				}
+				if (mi is MenuBarItem menuBarItem && !menuBarItem.IsTopLevel && FindAndOpenMenuByShortcut (kb, menuBarItem.Children)) {
+					return true;
+				}
+			}
+
+			return false;
+		}
+
 		private void ProcessMenu (int i, MenuBarItem mi)
 		{
 			if (mi.IsTopLevel) {
@@ -1293,7 +1388,7 @@ namespace Terminal.Gui {
 				OnKeyDown (kb);
 				OnKeyUp (kb);
 				return true;
-			} else if (kb.IsAlt) {
+			} else if (kb.IsAlt && !kb.IsCtrl && !kb.IsShift) {
 				if (FindAndOpenMenuByHotkey (kb)) return true;
 			}
 			//var kc = kb.KeyValue;
@@ -1315,7 +1410,7 @@ namespace Terminal.Gui {
 				break;
 
 			case Key.Esc:
-			case Key.ControlC:
+			case Key.C | Key.CtrlMask:
 				//TODO: Running = false;
 				CloseMenu ();
 				if (openedByAltKey) {
@@ -1358,6 +1453,12 @@ namespace Terminal.Gui {
 			return true;
 		}
 
+		///<inheritdoc/>
+		public override bool ProcessColdKey (KeyEvent kb)
+		{
+			return FindAndOpenMenuByShortcut (kb);
+		}
+
 		///<inheritdoc/>
 		public override bool MouseEvent (MouseEvent me)
 		{
@@ -1372,7 +1473,7 @@ namespace Terminal.Gui {
 				int pos = 1;
 				int cx = me.X;
 				for (int i = 0; i < Menus.Length; i++) {
-					if (cx >= pos && cx < pos + 1 + Menus [i].TitleLength + 2) {
+					if (cx >= pos && cx < pos + 1 + Menus [i].TitleLength + Menus [i].Help.RuneCount + 2) {
 						if (me.Flags == MouseFlags.Button1Clicked) {
 							if (Menus [i].IsTopLevel) {
 								var menu = new Menu (this, i, 0, Menus [i]);
@@ -1424,6 +1525,7 @@ namespace Terminal.Gui {
 						};
 
 						v.MouseEvent (nme);
+						return false;
 					}
 				} else if (!(me.View is MenuBar || me.View is Menu) && (me.Flags.HasFlag (MouseFlags.Button1Clicked) ||
 					me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked || me.Flags == MouseFlags.Button1TripleClicked)) {

+ 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);
 

+ 21 - 12
Terminal.Gui/Views/TextField.cs

@@ -352,9 +352,9 @@ namespace Terminal.Gui {
 			// Needed for the Elmish Wrapper issue https://github.com/DieselMeister/Terminal.Gui.Elmish/issues/2
 			var oldCursorPos = point;
 
-			switch (kb.Key) {
+			switch (ShortcutHelper.GetModifiersKey (kb)) {
 			case Key.DeleteChar:
-			case Key.ControlD:
+			case Key.D | Key.CtrlMask:
 				if (ReadOnly)
 					return true;
 
@@ -388,6 +388,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;
@@ -396,6 +398,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;
@@ -405,7 +409,8 @@ namespace Terminal.Gui {
 
 			// Home, C-A
 			case Key.Home:
-			case Key.ControlA:
+			case Key.Home | Key.CtrlMask:
+			case Key.A | Key.CtrlMask:
 				ClearAllSelection ();
 				point = 0;
 				Adjust ();
@@ -448,7 +453,7 @@ namespace Terminal.Gui {
 				break;
 
 			case Key.CursorLeft:
-			case Key.ControlB:
+			case Key.B | Key.CtrlMask:
 				ClearAllSelection ();
 				if (point > 0) {
 					point--;
@@ -457,14 +462,15 @@ namespace Terminal.Gui {
 				break;
 
 			case Key.End:
-			case Key.ControlE: // End
+			case Key.End | Key.CtrlMask:
+			case Key.E | Key.CtrlMask: // End
 				ClearAllSelection ();
 				point = text.Count;
 				Adjust ();
 				break;
 
 			case Key.CursorRight:
-			case Key.ControlF:
+			case Key.F | Key.CtrlMask:
 				ClearAllSelection ();
 				if (point == text.Count)
 					break;
@@ -472,7 +478,7 @@ namespace Terminal.Gui {
 				Adjust ();
 				break;
 
-			case Key.ControlK: // kill-to-end
+			case Key.K | Key.CtrlMask: // kill-to-end
 				if (ReadOnly)
 					return true;
 
@@ -485,7 +491,7 @@ namespace Terminal.Gui {
 				break;
 
 			// Undo
-			case Key.ControlZ:
+			case Key.Z | Key.CtrlMask:
 				if (ReadOnly)
 					return true;
 
@@ -501,7 +507,7 @@ namespace Terminal.Gui {
 				break;
 
 			//Redo
-			case Key.ControlY: // Control-y, yank
+			case Key.Y | Key.CtrlMask: // Control-y, yank
 				if (ReadOnly)
 					return true;
 
@@ -537,6 +543,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);
@@ -546,6 +553,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);
@@ -559,18 +567,18 @@ namespace Terminal.Gui {
 				SetNeedsDisplay ();
 				break;
 
-			case Key.ControlC:
+			case Key.C | Key.CtrlMask:
 				Copy ();
 				break;
 
-			case Key.ControlX:
+			case Key.X | Key.CtrlMask:
 				if (ReadOnly)
 					return true;
 
 				Cut ();
 				break;
 
-			case Key.ControlV:
+			case Key.V | Key.CtrlMask:
 				Paste ();
 				break;
 
@@ -824,6 +832,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);
 

+ 571 - 387
UICatalog/Scenarios/DynamicMenuBar.cs

@@ -19,7 +19,7 @@ namespace UICatalog {
 		}
 	}
 
-	class DynamicMenuItemList {
+	public class DynamicMenuItemList {
 		public ustring Title { get; set; }
 		public MenuItem MenuItem { get; set; }
 
@@ -34,22 +34,24 @@ namespace UICatalog {
 		public override string ToString () => $"{Title}, {MenuItem}";
 	}
 
-	class DynamicMenuItem {
+	public class DynamicMenuItem {
 		public ustring title = "_New";
 		public ustring help = "";
 		public ustring action = "";
 		public bool isTopLevel;
 		public bool hasSubMenu;
 		public MenuItemCheckStyle checkStyle;
+		public ustring shortcut;
 
 		public DynamicMenuItem () { }
 
-		public DynamicMenuItem (ustring title)
+		public DynamicMenuItem (ustring title, bool hasSubMenu = false)
 		{
 			this.title = title;
+			this.hasSubMenu = hasSubMenu;
 		}
 
-		public DynamicMenuItem (ustring title, ustring help, ustring action, bool isTopLevel, bool hasSubMenu, MenuItemCheckStyle checkStyle = MenuItemCheckStyle.NoCheck)
+		public DynamicMenuItem (ustring title, ustring help, ustring action, bool isTopLevel, bool hasSubMenu, MenuItemCheckStyle checkStyle = MenuItemCheckStyle.NoCheck, ustring shortcut = null)
 		{
 			this.title = title;
 			this.help = help;
@@ -57,14 +59,16 @@ namespace UICatalog {
 			this.isTopLevel = isTopLevel;
 			this.hasSubMenu = hasSubMenu;
 			this.checkStyle = checkStyle;
+			this.shortcut = shortcut;
 		}
 	}
 
-	class DynamicMenuBarSample : Window {
+	public class DynamicMenuBarSample : Window {
 		MenuBar _menuBar;
 		MenuItem _currentMenuBarItem;
 		int _currentSelectedMenuBar;
 		MenuItem _currentEditMenuBarItem;
+		ListView _lstMenus;
 
 		public DynamicMenuItemModel DataContext { get; set; }
 
@@ -72,6 +76,22 @@ namespace UICatalog {
 		{
 			DataContext = new DynamicMenuItemModel ();
 
+			var _frmDelimiter = new FrameView ("Shortcut Delimiter:") {
+				X = Pos.Center (),
+				Y = 3,
+				Width = 25,
+				Height = 4
+			};
+
+			var _txtDelimiter = new TextField (MenuBar.ShortcutDelimiter.ToString ()) {
+				X = Pos.Center(),
+				Width = 2,
+			};
+			_txtDelimiter.TextChanged += (_) => MenuBar.ShortcutDelimiter = _txtDelimiter.Text;
+			_frmDelimiter.Add (_txtDelimiter);
+
+			Add (_frmDelimiter);
+
 			var _frmMenu = new FrameView ("Menus:") {
 				Y = 7,
 				Width = Dim.Percent (50),
@@ -144,7 +164,7 @@ namespace UICatalog {
 			};
 			_frmMenu.Add (_btnPreviowsParent);
 
-			var _lstMenus = new ListView (new List<DynamicMenuItemList> ()) {
+			_lstMenus = new ListView (new List<DynamicMenuItemList> ()) {
 				ColorScheme = Colors.Dialog,
 				X = Pos.Right (_btnPrevious) + 1,
 				Y = Pos.Top (_btnPrevious) + 2,
@@ -178,125 +198,127 @@ namespace UICatalog {
 
 			Add (_frmMenu);
 
-			var _frmMenuDetails = new FrameView ("Menu Details:") {
+
+			var _frmMenuDetails = new DynamicMenuBarDetails ("Menu Details:") {
 				X = Pos.Right (_frmMenu),
 				Y = Pos.Top (_frmMenu),
 				Width = Dim.Fill (),
-				Height = Dim.Fill ()
+				Height = Dim.Fill (2)
 			};
+			Add (_frmMenuDetails);
 
-			var _lblTitle = new Label ("Title:") {
-				Y = 1
+			_btnMenuBarUp.Clicked += () => {
+				var i = _currentSelectedMenuBar;
+				var menuItem = _menuBar != null && _menuBar.Menus.Length > 0 ? _menuBar.Menus [i] : null;
+				if (menuItem != null) {
+					var menus = _menuBar.Menus;
+					if (i > 0) {
+						menus [i] = menus [i - 1];
+						menus [i - 1] = menuItem;
+						_currentSelectedMenuBar = i - 1;
+						_menuBar.SetNeedsDisplay ();
+					}
+				}
 			};
-			_frmMenuDetails.Add (_lblTitle);
 
-			var _txtTitle = new TextField () {
-				X = Pos.Right (_lblTitle) + 2,
-				Y = Pos.Top (_lblTitle),
-				Width = Dim.Fill ()
+			_btnMenuBarDown.Clicked += () => {
+				var i = _currentSelectedMenuBar;
+				var menuItem = _menuBar != null && _menuBar.Menus.Length > 0 ? _menuBar.Menus [i] : null;
+				if (menuItem != null) {
+					var menus = _menuBar.Menus;
+					if (i < menus.Length - 1) {
+						menus [i] = menus [i + 1];
+						menus [i + 1] = menuItem;
+						_currentSelectedMenuBar = i + 1;
+						_menuBar.SetNeedsDisplay ();
+					}
+				}
 			};
-			_frmMenuDetails.Add (_txtTitle);
 
-			var _lblHelp = new Label ("Help:") {
-				X = Pos.Left (_lblTitle),
-				Y = Pos.Bottom (_lblTitle) + 1
+			_btnUp.Clicked += () => {
+				var i = _lstMenus.SelectedItem;
+				var menuItem = DataContext.Menus.Count > 0 ? DataContext.Menus [i].MenuItem : null;
+				if (menuItem != null) {
+					var childrens = ((MenuBarItem)_currentMenuBarItem).Children;
+					if (i > 0) {
+						childrens [i] = childrens [i - 1];
+						childrens [i - 1] = menuItem;
+						DataContext.Menus [i] = DataContext.Menus [i - 1];
+						DataContext.Menus [i - 1] = new DynamicMenuItemList (menuItem.Title, menuItem);
+						_lstMenus.SelectedItem = i - 1;
+					}
+				}
 			};
-			_frmMenuDetails.Add (_lblHelp);
 
-			var _txtHelp = new TextField () {
-				X = Pos.Left (_txtTitle),
-				Y = Pos.Top (_lblHelp),
-				Width = Dim.Fill ()
+			_btnDown.Clicked += () => {
+				var i = _lstMenus.SelectedItem;
+				var menuItem = DataContext.Menus.Count > 0 ? DataContext.Menus [i].MenuItem : null;
+				if (menuItem != null) {
+					var childrens = ((MenuBarItem)_currentMenuBarItem).Children;
+					if (i < childrens.Length - 1) {
+						childrens [i] = childrens [i + 1];
+						childrens [i + 1] = menuItem;
+						DataContext.Menus [i] = DataContext.Menus [i + 1];
+						DataContext.Menus [i + 1] = new DynamicMenuItemList (menuItem.Title, menuItem);
+						_lstMenus.SelectedItem = i + 1;
+					}
+				}
 			};
-			_frmMenuDetails.Add (_txtHelp);
 
-			var _lblAction = new Label ("Action:") {
-				X = Pos.Left (_lblTitle),
-				Y = Pos.Bottom (_lblHelp) + 1
+			_btnPreviowsParent.Clicked += () => {
+				if (_currentMenuBarItem != null && _currentMenuBarItem.Parent != null) {
+					var mi = _currentMenuBarItem;
+					_currentMenuBarItem = _currentMenuBarItem.Parent as MenuBarItem;
+					SetListViewSource (_currentMenuBarItem, true);
+					var i = ((MenuBarItem)_currentMenuBarItem).GetChildrenIndex (mi);
+					if (i > -1) {
+						_lstMenus.SelectedItem = i;
+					}
+					if (_currentMenuBarItem.Parent != null) {
+						DataContext.Parent = _currentMenuBarItem.Title;
+					} else {
+						DataContext.Parent = ustring.Empty;
+					}
+				} else {
+					DataContext.Parent = ustring.Empty;
+				}
 			};
-			_frmMenuDetails.Add (_lblAction);
 
-			var _txtAction = new TextView () {
-				ColorScheme = Colors.Dialog,
-				X = Pos.Left (_txtTitle),
-				Y = Pos.Top (_lblAction),
-				Width = Dim.Fill (),
-				Height = 5
-			};
-			_frmMenuDetails.Add (_txtAction);
 
-			var _ckbIsTopLevel = new CheckBox ("IsTopLevel") {
-				X = Pos.Left (_lblTitle),
-				Y = Pos.Bottom (_lblAction) + 5
+			var _btnOk = new Button ("Ok") {
+				X = Pos.Right (_frmMenu) + 20,
+				Y = Pos.Bottom (_frmMenuDetails),
 			};
-			_frmMenuDetails.Add (_ckbIsTopLevel);
+			Add (_btnOk);
 
-			var _ckbSubMenu = new CheckBox ("Has sub-menus") {
-				X = Pos.Left (_lblTitle),
-				Y = Pos.Bottom (_ckbIsTopLevel)
-			};
-			_frmMenuDetails.Add (_ckbSubMenu);
-			_ckbIsTopLevel.Toggled += (e) => {
-				if (_ckbIsTopLevel.Checked && _currentEditMenuBarItem.Parent != null) {
-					MessageBox.ErrorQuery ("Invalid IsTopLevel", "Only menu bar can have top level menu item!", "Ok");
-					_ckbIsTopLevel.Checked = false;
-					return;
-				}
-				if (_ckbIsTopLevel.Checked) {
-					_ckbSubMenu.Checked = false;
-					_ckbSubMenu.SetNeedsDisplay ();
-					_txtAction.ReadOnly = false;
-				} else {
-					_txtAction.ReadOnly = true;
-				}
+			var _btnCancel = new Button ("Cancel") {
+				X = Pos.Right (_btnOk) + 3,
+				Y = Pos.Top (_btnOk),
 			};
-			_ckbSubMenu.Toggled += (e) => {
-				if (_ckbSubMenu.Checked) {
-					_ckbIsTopLevel.Checked = false;
-					_ckbIsTopLevel.SetNeedsDisplay ();
-					_txtAction.ReadOnly = true;
-				} else {
-					_txtAction.ReadOnly = false;
-				}
+			_btnCancel.Clicked += () => {
+				SetFrameDetails (_currentEditMenuBarItem);
 			};
+			Add (_btnCancel);
 
-			var _rChkLabels = new ustring [] { "NoCheck", "Checked", "Radio" };
-			var _rbChkStyle = new RadioGroup (_rChkLabels) {
-				X = Pos.Left (_lblTitle),
-				Y = Pos.Bottom (_ckbSubMenu) + 1,
+			_lstMenus.SelectedItemChanged += (e) => {
+				SetFrameDetails ();
 			};
-			_frmMenuDetails.Add (_rbChkStyle);
 
-			var _btnOk = new Button ("Ok") {
-				X = Pos.Left (_lblTitle) + 20,
-				Y = Pos.Bottom (_rbChkStyle) + 1,
-			};
 			_btnOk.Clicked += () => {
-				if (ustring.IsNullOrEmpty (_txtTitle.Text) && _currentEditMenuBarItem != null) {
+				if (ustring.IsNullOrEmpty (_frmMenuDetails._txtTitle.Text) && _currentEditMenuBarItem != null) {
 					MessageBox.ErrorQuery ("Invalid title", "Must enter a valid title!.", "Ok");
 				} else if (_currentEditMenuBarItem != null) {
-					var menuItem = new DynamicMenuItem (_txtTitle.Text, _txtHelp.Text,
-						_txtAction.Text,
-						_ckbIsTopLevel != null ? _ckbIsTopLevel.Checked : false,
-						_ckbSubMenu != null ? _ckbSubMenu.Checked : false,
-						_rbChkStyle.SelectedItem == 0 ? MenuItemCheckStyle.NoCheck :
-						_rbChkStyle.SelectedItem == 1 ? MenuItemCheckStyle.Checked :
-						MenuItemCheckStyle.Radio);
+					var menuItem = new DynamicMenuItem (_frmMenuDetails._txtTitle.Text, _frmMenuDetails._txtHelp.Text,
+						_frmMenuDetails._txtAction.Text,
+						_frmMenuDetails._ckbIsTopLevel != null ? _frmMenuDetails._ckbIsTopLevel.Checked : false,
+						_frmMenuDetails._ckbSubMenu != null ? _frmMenuDetails._ckbSubMenu.Checked : false,
+						_frmMenuDetails._rbChkStyle.SelectedItem == 0 ? MenuItemCheckStyle.NoCheck :
+						_frmMenuDetails._rbChkStyle.SelectedItem == 1 ? MenuItemCheckStyle.Checked :
+						MenuItemCheckStyle.Radio,
+						_frmMenuDetails._txtShortcut.Text);
 					UpdateMenuItem (_currentEditMenuBarItem, menuItem, _lstMenus.SelectedItem);
 				}
 			};
-			_frmMenuDetails.Add (_btnOk);
-
-			var _btnCancel = new Button ("Cancel") {
-				X = Pos.Right (_btnOk) + 3,
-				Y = Pos.Top (_btnOk),
-			};
-			_btnCancel.Clicked += () => {
-				_txtTitle.Text = ustring.Empty;
-			};
-			_frmMenuDetails.Add (_btnCancel);
-
-			Add (_frmMenuDetails);
 
 			_btnAdd.Clicked += () => {
 				if (MenuBar == null) {
@@ -305,15 +327,16 @@ namespace UICatalog {
 					return;
 				}
 
-				var item = EnterMenuItem (_currentMenuBarItem);
-				if (ustring.IsNullOrEmpty (item.title)) {
+				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 [] { new MenuItem ("_New", "", CreateAction (_currentEditMenuBarItem, new DynamicMenuItem ())) }, _currentMenuBarItem.Parent);
+					_currentMenuBarItem = new MenuBarItem (_currentMenuBarItem.Title, new MenuItem [] { }, _currentMenuBarItem.Parent);
 					_currentMenuBarItem.CheckType = item.checkStyle;
 					parent.Children [idx] = _currentMenuBarItem;
 				} else {
@@ -350,78 +373,65 @@ namespace UICatalog {
 					if (childrens.Length == 0) {
 						if (_currentMenuBarItem.Parent == null) {
 							((MenuBarItem)_currentMenuBarItem).Children = null;
-							_currentMenuBarItem.Action = CreateAction (_currentEditMenuBarItem, new DynamicMenuItem (_currentMenuBarItem.Title));
+							//_currentMenuBarItem.Action = _frmMenuDetails.CreateAction (_currentEditMenuBarItem, new DynamicMenuItem (_currentMenuBarItem.Title));
 						} else {
-							_currentMenuBarItem = new MenuItem (_currentMenuBarItem.Title, _currentMenuBarItem.Help, CreateAction (_currentEditMenuBarItem, new DynamicMenuItem (_currentEditMenuBarItem.Title)), null, _currentMenuBarItem.Parent);
+							_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 ();
 				}
 			};
 
-			_btnMenuBarUp.Clicked += () => {
-				var i = _currentSelectedMenuBar;
-				var menuItem = _menuBar != null && _menuBar.Menus.Length > 0 ? _menuBar.Menus [i] : null;
-				if (menuItem != null) {
-					var menus = _menuBar.Menus;
-					if (i > 0) {
-						menus [i] = menus [i - 1];
-						menus [i - 1] = menuItem;
-						_currentSelectedMenuBar = i - 1;
-						_menuBar.SetNeedsDisplay ();
-					}
+			_lstMenus.OpenSelectedItem += (e) => {
+				_currentMenuBarItem = DataContext.Menus [e.Item].MenuItem;
+				if (!(_currentMenuBarItem is MenuBarItem)) {
+					MessageBox.ErrorQuery ("Menu Open Error", "Must allows sub menus first!", "Ok");
+					return;
 				}
+				DataContext.Parent = _currentMenuBarItem.Title;
+				DataContext.Menus = new List<DynamicMenuItemList> ();
+				SetListViewSource (_currentMenuBarItem, true);
+				var menuBarItem = DataContext.Menus.Count > 0 ? DataContext.Menus [0].MenuItem : null;
+				SetFrameDetails (menuBarItem);
 			};
 
-			_btnMenuBarDown.Clicked += () => {
-				var i = _currentSelectedMenuBar;
-				var menuItem = _menuBar != null && _menuBar.Menus.Length > 0 ? _menuBar.Menus [i] : null;
-				if (menuItem != null) {
-					var menus = _menuBar.Menus;
-					if (i < menus.Length - 1) {
-						menus [i] = menus [i + 1];
-						menus [i + 1] = menuItem;
-						_currentSelectedMenuBar = i + 1;
-						_menuBar.SetNeedsDisplay ();
-					}
+			_lstMenus.Enter += (_) => {
+				var menuBarItem = DataContext.Menus.Count > 0 ? DataContext.Menus [_lstMenus.SelectedItem].MenuItem : null;
+				SetFrameDetails (menuBarItem);
+			};
+
+			_btnNext.Clicked += () => {
+				if (_menuBar != null && _currentSelectedMenuBar + 1 < _menuBar.Menus.Length) {
+					_currentSelectedMenuBar++;
 				}
+				SelectCurrentMenuBarItem ();
 			};
 
-			_btnUp.Clicked += () => {
-				var i = _lstMenus.SelectedItem;
-				var menuItem = DataContext.Menus.Count > 0 ? DataContext.Menus [i].MenuItem : null;
-				if (menuItem != null) {
-					var childrens = ((MenuBarItem)_currentMenuBarItem).Children;
-					if (i > 0) {
-						childrens [i] = childrens [i - 1];
-						childrens [i - 1] = menuItem;
-						DataContext.Menus [i] = DataContext.Menus [i - 1];
-						DataContext.Menus [i - 1] = new DynamicMenuItemList (menuItem.Title, menuItem);
-						_lstMenus.SelectedItem = i - 1;
-					}
+			_btnPrevious.Clicked += () => {
+				if (_currentSelectedMenuBar - 1 > -1) {
+					_currentSelectedMenuBar--;
 				}
+				SelectCurrentMenuBarItem ();
 			};
 
-			_btnDown.Clicked += () => {
-				var i = _lstMenus.SelectedItem;
-				var menuItem = DataContext.Menus.Count > 0 ? DataContext.Menus [i].MenuItem : null;
-				if (menuItem != null) {
-					var childrens = ((MenuBarItem)_currentMenuBarItem).Children;
-					if (i < childrens.Length - 1) {
-						childrens [i] = childrens [i + 1];
-						childrens [i + 1] = menuItem;
-						DataContext.Menus [i] = DataContext.Menus [i + 1];
-						DataContext.Menus [i + 1] = new DynamicMenuItemList (menuItem.Title, menuItem);
-						_lstMenus.SelectedItem = i + 1;
-					}
+			_lblMenuBar.Enter += (e) => {
+				if (_menuBar?.Menus != null) {
+					_currentMenuBarItem = _menuBar.Menus [_currentSelectedMenuBar];
+					SetFrameDetails (_menuBar.Menus [_currentSelectedMenuBar]);
 				}
 			};
 
 			_btnAddMenuBar.Clicked += () => {
-				var item = EnterMenuItem (null);
-				if (ustring.IsNullOrEmpty (item.title)) {
+				var frameDetails = new DynamicMenuBarDetails (null, false);
+				var item = frameDetails.EnterMenuItem ();
+				if (item == null) {
 					return;
 				}
 
@@ -441,11 +451,15 @@ namespace UICatalog {
 				_menuBar.Menus [_currentSelectedMenuBar] = newMenu;
 				_lblMenuBar.Text = newMenu.Title;
 				SetListViewSource (_currentMenuBarItem, true);
-				EditMenuBarItem (_menuBar.Menus [_currentSelectedMenuBar]);
+				SetFrameDetails (_menuBar.Menus [_currentSelectedMenuBar]);
 				_menuBar.SetNeedsDisplay ();
 			};
 
 			_btnRemoveMenuBar.Clicked += () => {
+				if (_menuBar == null || _menuBar.Menus.Length == 0) {
+					return;
+				}
+
 				if (_menuBar != null && _menuBar.Menus.Length > 0) {
 					_menuBar.Menus [_currentSelectedMenuBar] = null;
 					int i = 0;
@@ -474,62 +488,12 @@ namespace UICatalog {
 					_lblMenuBar.Text = _menuBar.Menus [_currentSelectedMenuBar].Title;
 				}
 				SetListViewSource (_currentMenuBarItem, true);
-				EditMenuBarItem (null);
+				SetFrameDetails (null);
 			};
 
-			_lblMenuBar.Enter += (e) => {
-				if (_menuBar?.Menus != null) {
-					_currentMenuBarItem = _menuBar.Menus [_currentSelectedMenuBar];
-					EditMenuBarItem (_menuBar.Menus [_currentSelectedMenuBar]);
-				}
-			};
-
-			_btnPrevious.Clicked += () => {
-				if (_currentSelectedMenuBar - 1 > -1) {
-					_currentSelectedMenuBar--;
-				}
-				SelectCurrentMenuBarItem ();
-			};
 
-			_btnNext.Clicked += () => {
-				if (_menuBar != null && _currentSelectedMenuBar + 1 < _menuBar.Menus.Length) {
-					_currentSelectedMenuBar++;
-				}
-				SelectCurrentMenuBarItem ();
-			};
-
-			_lstMenus.SelectedItemChanged += (e) => {
-				var menuBarItem = DataContext.Menus.Count > 0 ? DataContext.Menus [e.Item].MenuItem : null;
-				EditMenuBarItem (menuBarItem);
-			};
-
-			_lstMenus.OpenSelectedItem += (e) => {
-				_currentMenuBarItem = DataContext.Menus [e.Item].MenuItem;
-				DataContext.Parent = _currentMenuBarItem.Title;
-				DataContext.Menus = new List<DynamicMenuItemList> ();
-				SetListViewSource (_currentMenuBarItem, true);
-				var menuBarItem = DataContext.Menus.Count > 0 ? DataContext.Menus [0].MenuItem : null;
-				EditMenuBarItem (menuBarItem);
-			};
+			SetFrameDetails ();
 
-			_btnPreviowsParent.Clicked += () => {
-				if (_currentMenuBarItem != null && _currentMenuBarItem.Parent != null) {
-					var mi = _currentMenuBarItem;
-					_currentMenuBarItem = _currentMenuBarItem.Parent as MenuBarItem;
-					SetListViewSource (_currentMenuBarItem, true);
-					var i = ((MenuBarItem)_currentMenuBarItem).GetChildrenIndex (mi);
-					if (i > -1) {
-						_lstMenus.SelectedItem = i;
-					}
-					if (_currentMenuBarItem.Parent != null) {
-						DataContext.Parent = _currentMenuBarItem.Title;
-					} else {
-						DataContext.Parent = ustring.Empty;
-					}
-				} else {
-					DataContext.Parent = ustring.Empty;
-				}
-			};
 
 			var ustringConverter = new UStringValueConverter ();
 			var listWrapperConverter = new ListWrapperConverter ();
@@ -539,45 +503,37 @@ namespace UICatalog {
 			var lstMenus = new Binding (this, "Menus", _lstMenus, "Source", listWrapperConverter);
 
 
-			ustring GetTargetAction (Action action)
+			void SetFrameDetails (MenuItem menuBarItem = null)
 			{
-				var me = action.Target;
+				MenuItem menuItem;
 
-				if (me == null) {
-					throw new ArgumentException ();
+				if (menuBarItem == null) {
+					menuItem = DataContext.Menus.Count > 0 ? DataContext.Menus [_lstMenus.SelectedItem].MenuItem : null;
+				} else {
+					menuItem = menuBarItem;
 				}
-				object v = new object ();
-				foreach (var field in me.GetType ().GetFields ()) {
-					if (field.Name == "item") {
-						v = field.GetValue (me);
-					}
+
+				_currentEditMenuBarItem = menuItem;
+				_frmMenuDetails.EditMenuBarItem (menuItem);
+				var f = _btnOk.CanFocus == _frmMenuDetails.CanFocus;
+				if (!f) {
+					_btnOk.CanFocus = _frmMenuDetails.CanFocus;
+					_btnCancel.CanFocus = _frmMenuDetails.CanFocus;
 				}
-				return v == null || !(v is DynamicMenuItem item) ? ustring.Empty : item.action;
 			}
 
-			Action CreateAction (MenuItem menuItem, DynamicMenuItem item)
+			void SelectCurrentMenuBarItem ()
 			{
-				switch (menuItem.CheckType) {
-				case MenuItemCheckStyle.NoCheck:
-					return new Action (() => MessageBox.ErrorQuery (item.title, item.action, "Ok"));
-				case MenuItemCheckStyle.Checked:
-					return new Action (() => menuItem.Checked = !menuItem.Checked);
-				case MenuItemCheckStyle.Radio:
-					break;
-				}
-				return new Action (() => {
-					menuItem.Checked = true;
-					var parent = menuItem?.Parent as MenuBarItem;
-					if (parent != null) {
-						var childrens = parent.Children;
-						for (int i = 0; i < childrens.Length; i++) {
-							var child = childrens [i];
-							if (child != menuItem) {
-								child.Checked = false;
-							}
-						}
-					}
-				});
+				MenuBarItem menuBarItem = null;
+				if (_menuBar?.Menus != null) {
+					menuBarItem = _menuBar.Menus [_currentSelectedMenuBar];
+					_lblMenuBar.Text = menuBarItem.Title;
+				}
+				SetFrameDetails (menuBarItem);
+				_currentMenuBarItem = menuBarItem;
+				DataContext.Menus = new List<DynamicMenuItemList> ();
+				SetListViewSource (_currentMenuBarItem, true);
+				_lblParent.Text = ustring.Empty;
 			}
 
 			void SetListViewSource (MenuItem _currentMenuBarItem, bool fill = false)
@@ -598,20 +554,26 @@ namespace UICatalog {
 				}
 			}
 
-			void EditMenuBarItem (MenuItem menuBarItem)
+			MenuItem CreateNewMenu (DynamicMenuItem item, MenuItem parent = null)
 			{
-				if (menuBarItem == null) {
-					_frmMenuDetails.CanFocus = false;
+				MenuItem newMenu;
+				if (item.hasSubMenu) {
+					newMenu = new MenuBarItem (item.title, new MenuItem [] { }, parent);
+				} else if (parent != null) {
+					newMenu = new MenuItem (item.title, item.help, null, null, parent);
+					newMenu.CheckType = item.checkStyle;
+					newMenu.Action = _frmMenuDetails.CreateAction (newMenu, item);
+					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 {
-					_frmMenuDetails.CanFocus = true;
-				}
-				_currentEditMenuBarItem = menuBarItem;
-				_txtTitle.Text = menuBarItem?.Title ?? "";
-				_txtHelp.Text = menuBarItem?.Help ?? "";
-				_txtAction.Text = menuBarItem != null && menuBarItem.Action != null ? GetTargetAction (menuBarItem.Action) : ustring.Empty;
-				_ckbIsTopLevel.Checked = IsTopLevel (menuBarItem);
-				_ckbSubMenu.Checked = HasSubMenus (menuBarItem);
-				_rbChkStyle.SelectedItem = (int)(menuBarItem?.CheckType ?? MenuItemCheckStyle.NoCheck);
+					newMenu = new MenuBarItem (item.title, item.help, null);
+					((MenuBarItem)newMenu).Children [0].Action = _frmMenuDetails.CreateAction (newMenu, item);
+					((MenuBarItem)newMenu).Children [0].Shortcut = ShortcutHelper.GetShortcutFromTag (item.shortcut);
+				}
+
+				return newMenu;
 			}
 
 			void UpdateMenuItem (MenuItem _currentEditMenuBarItem, DynamicMenuItem menuItem, int index)
@@ -625,199 +587,421 @@ namespace UICatalog {
 				}
 				if (menuItem.isTopLevel && _currentEditMenuBarItem is MenuBarItem) {
 					((MenuBarItem)_currentEditMenuBarItem).Children = null;
-					_currentEditMenuBarItem.Action = CreateAction (_currentEditMenuBarItem, menuItem);
+					_currentEditMenuBarItem.Action = _frmMenuDetails.CreateAction (_currentEditMenuBarItem, menuItem);
 					SetListViewSource (_currentEditMenuBarItem, true);
 				} else if (menuItem.hasSubMenu) {
 					_currentEditMenuBarItem.Action = null;
 					if (_currentEditMenuBarItem is MenuBarItem && ((MenuBarItem)_currentEditMenuBarItem).Children == null) {
-						((MenuBarItem)_currentEditMenuBarItem).Children = new MenuItem [] { new MenuItem ("_New", "", CreateAction (_currentEditMenuBarItem, new DynamicMenuItem ())) };
+						((MenuBarItem)_currentEditMenuBarItem).Children = new MenuItem [] { };
 					} else if (_currentEditMenuBarItem.Parent != null) {
-						UpdateParent (ref _currentEditMenuBarItem);
+						_frmMenuDetails.UpdateParent (ref _currentEditMenuBarItem);
 					} else {
-						_currentEditMenuBarItem = new MenuBarItem (_currentEditMenuBarItem.Title, new MenuItem [] { new MenuItem ("_New", "", CreateAction (_currentEditMenuBarItem, new DynamicMenuItem ())) }, _currentEditMenuBarItem.Parent);
+						_currentEditMenuBarItem = new MenuBarItem (_currentEditMenuBarItem.Title, new MenuItem [] { }, _currentEditMenuBarItem.Parent);
 					}
 					SetListViewSource (_currentEditMenuBarItem, true);
 				} else if (_currentEditMenuBarItem is MenuBarItem && _currentEditMenuBarItem.Parent != null) {
-					UpdateParent (ref _currentEditMenuBarItem);
-					_currentEditMenuBarItem = new MenuItem (menuItem.title, menuItem.help, CreateAction (_currentEditMenuBarItem, menuItem), null, _currentEditMenuBarItem.Parent);
+					_frmMenuDetails.UpdateParent (ref _currentEditMenuBarItem);
+					_currentEditMenuBarItem = new MenuItem (menuItem.title, menuItem.help, _frmMenuDetails.CreateAction (_currentEditMenuBarItem, menuItem), null, _currentEditMenuBarItem.Parent);
 				} else {
 					if (_currentEditMenuBarItem is MenuBarItem) {
 						((MenuBarItem)_currentEditMenuBarItem).Children = null;
 						DataContext.Menus = new List<DynamicMenuItemList> ();
 					}
-					_currentEditMenuBarItem.Action = CreateAction (_currentEditMenuBarItem, menuItem);
+					_currentEditMenuBarItem.Action = _frmMenuDetails.CreateAction (_currentEditMenuBarItem, menuItem);
+					_currentEditMenuBarItem.Shortcut = ShortcutHelper.GetShortcutFromTag (menuItem.shortcut);
 				}
 
 				if (_currentEditMenuBarItem.Parent == null) {
 					DataContext.MenuBar = _currentEditMenuBarItem.Title;
 				} else {
+					if (DataContext.Menus.Count == 0) {
+						DataContext.Menus.Add (new DynamicMenuItemList (_currentEditMenuBarItem.Title, _currentEditMenuBarItem));
+					}
 					DataContext.Menus [index] = new DynamicMenuItemList (_currentEditMenuBarItem.Title, _currentEditMenuBarItem);
 				}
 				_currentEditMenuBarItem.CheckType = menuItem.checkStyle;
-				EditMenuBarItem (_currentEditMenuBarItem);
+				SetFrameDetails (_currentEditMenuBarItem);
 			}
 
-			void UpdateParent (ref MenuItem menuItem)
-			{
-				var parent = menuItem.Parent as MenuBarItem;
-				var idx = parent.GetChildrenIndex (menuItem);
-				if (!(menuItem is MenuBarItem)) {
-					menuItem = new MenuBarItem (menuItem.Title, new MenuItem [] { new MenuItem ("_New", "", CreateAction (menuItem, new DynamicMenuItem ())) }, menuItem.Parent);
-					if (idx > -1) {
-						parent.Children [idx] = menuItem;
-					}
-				} else {
-					menuItem = new MenuItem (menuItem.Title, menuItem.Help, CreateAction (menuItem, new DynamicMenuItem ()), null, menuItem.Parent);
-					if (idx > -1) {
-						parent.Children [idx] = menuItem;
-					}
+
+			_frmMenuDetails.Initialized += (s, e) => _frmMenuDetails.CanFocus = false;
+		}
+	}
+
+	public class DynamicMenuBarDetails : FrameView {
+		public MenuItem _menuItem;
+		public TextField _txtTitle;
+		public TextField _txtHelp;
+		public TextView _txtAction;
+		public CheckBox _ckbIsTopLevel;
+		public CheckBox _ckbSubMenu;
+		public RadioGroup _rbChkStyle;
+		public TextField _txtShortcut;
+
+		bool hasParent;
+
+		public DynamicMenuBarDetails (MenuItem menuItem = null, bool hasParent = false) : this (menuItem == null ? "Adding New Menu." : "Editing Menu.")
+		{
+			_menuItem = menuItem;
+			this.hasParent = hasParent;
+		}
+
+		public DynamicMenuBarDetails (ustring title) : base (title)
+		{
+			var _lblTitle = new Label ("Title:") {
+				Y = 1
+			};
+			Add (_lblTitle);
+
+			_txtTitle = new TextField () {
+				X = Pos.Right (_lblTitle) + 2,
+				Y = Pos.Top (_lblTitle),
+				Width = Dim.Fill ()
+			};
+			Add (_txtTitle);
+
+			var _lblHelp = new Label ("Help:") {
+				X = Pos.Left (_lblTitle),
+				Y = Pos.Bottom (_lblTitle) + 1
+			};
+			Add (_lblHelp);
+
+			_txtHelp = new TextField () {
+				X = Pos.Left (_txtTitle),
+				Y = Pos.Top (_lblHelp),
+				Width = Dim.Fill ()
+			};
+			Add (_txtHelp);
+
+			var _lblAction = new Label ("Action:") {
+				X = Pos.Left (_lblTitle),
+				Y = Pos.Bottom (_lblHelp) + 1
+			};
+			Add (_lblAction);
+
+			_txtAction = new TextView () {
+				ColorScheme = Colors.Dialog,
+				X = Pos.Left (_txtTitle),
+				Y = Pos.Top (_lblAction),
+				Width = Dim.Fill (),
+				Height = 5
+			};
+			Add (_txtAction);
+
+			_ckbIsTopLevel = new CheckBox ("IsTopLevel") {
+				X = Pos.Left (_lblTitle),
+				Y = Pos.Bottom (_lblAction) + 5
+			};
+			Add (_ckbIsTopLevel);
+
+			_ckbSubMenu = new CheckBox ("Has sub-menus") {
+				X = Pos.Left (_lblTitle),
+				Y = Pos.Bottom (_ckbIsTopLevel),
+				Checked = _menuItem == null ? !hasParent : HasSubMenus (_menuItem)
+			};
+			Add (_ckbSubMenu);
+
+			var _rChkLabels = new ustring [] { "NoCheck", "Checked", "Radio" };
+			_rbChkStyle = new RadioGroup (_rChkLabels) {
+				X = Pos.Left (_lblTitle),
+				Y = Pos.Bottom (_ckbSubMenu) + 1,
+			};
+			Add (_rbChkStyle);
+
+			var _lblShortcut = new Label ("Shortcut:") {
+				X = Pos.Right (_ckbSubMenu) + 10,
+				Y = Pos.Top (_ckbSubMenu)
+			};
+			Add (_lblShortcut);
+
+			_txtShortcut = new TextField () {
+				X = Pos.X (_lblShortcut),
+				Y = Pos.Bottom (_lblShortcut),
+				Width = Dim.Fill (),
+				ReadOnly = true
+			};
+			_txtShortcut.KeyDown += (e) => {
+				if (!ProcessKey (e.KeyEvent)) {
+					return;
 				}
-			}
 
-			bool IsTopLevel (MenuItem menuItem)
+				var k = ShortcutHelper.GetModifiersKey (e.KeyEvent);
+				if (CheckShortcut (k, true)) {
+					e.Handled = true;
+				}
+			};
+
+			bool ProcessKey (KeyEvent ev)
 			{
-				var topLevel = menuItem as MenuBarItem;
-				if (topLevel != null && topLevel.Parent == null && (topLevel.Children == null || topLevel.Children.Length == 0)) {
-					return true;
-				} else {
+				switch (ev.Key) {
+				case Key.CursorUp:
+				case Key.CursorDown:
+				case Key.Tab:
+				case Key.BackTab:
 					return false;
 				}
+
+				return true;
 			}
 
-			bool HasSubMenus (MenuItem menuItem)
+			bool CheckShortcut (Key k, bool pre)
 			{
-				var menuBarItem = menuItem as MenuBarItem;
-				if (menuBarItem != null && menuBarItem.Children != null && menuBarItem.Children.Length > 0) {
-					return true;
-				} else {
+				var m = _menuItem != null ? _menuItem : new MenuItem ();
+				if (pre && !ShortcutHelper.PreShortcutValidation (k)) {
+					_txtShortcut.Text = "";
 					return false;
 				}
+				if (!pre) {
+					if (!ShortcutHelper.PostShortcutValidation (ShortcutHelper.GetShortcutFromTag (_txtShortcut.Text))) {
+						_txtShortcut.Text = "";
+						return false;
+					}
+					return true;
+				}
+				_txtShortcut.Text = ShortcutHelper.GetShortcutTag (k);
+
+				return true;
 			}
 
-			void SelectCurrentMenuBarItem ()
-			{
-				MenuBarItem menuBarItem = null;
-				if (_menuBar?.Menus != null) {
-					menuBarItem = _menuBar.Menus [_currentSelectedMenuBar];
-					_lblMenuBar.Text = menuBarItem.Title;
+			_txtShortcut.KeyUp += (e) => {
+				var k = ShortcutHelper.GetModifiersKey (e.KeyEvent);
+				if (CheckShortcut (k, false)) {
+					e.Handled = true;
 				}
-				EditMenuBarItem (menuBarItem);
-				_currentMenuBarItem = menuBarItem;
-				DataContext.Menus = new List<DynamicMenuItemList> ();
-				SetListViewSource (_currentMenuBarItem, true);
-				_lblParent.Text = ustring.Empty;
-			}
+			};
+			Add (_txtShortcut);
 
-			DynamicMenuItem EnterMenuItem (MenuItem menuItem)
-			{
-				var _lblTitle = new Label (1, 3, "Title:");
-				var _txtTitle = new TextField ("_New") {
-					X = Pos.Right (_lblTitle) + 2,
-					Y = Pos.Top (_lblTitle),
-					Width = Dim.Fill (),
-				};
-				var _lblHelp = new Label ("Help:") {
-					X = Pos.Left (_lblTitle),
-					Y = Pos.Bottom (_lblTitle) + 1
-				};
-				var _txtHelp = new TextField () {
-					X = Pos.Left (_txtTitle),
-					Y = Pos.Top (_lblHelp),
-					Width = Dim.Fill (),
-				};
-				var _lblAction = new Label ("Action:") {
-					X = Pos.Left (_lblTitle),
-					Y = Pos.Bottom (_lblHelp) + 1
-				};
-				var _txtAction = new TextView () {
-					ColorScheme = Colors.Menu,
-					X = Pos.Left (_txtTitle),
-					Y = Pos.Top (_lblAction),
-					Width = Dim.Fill (),
-					Height = 5,
-					ReadOnly = true
-				};
-				var _ckbIsTopLevel = new CheckBox ("IsTopLevel") {
-					X = Pos.Left (_lblTitle),
-					Y = Pos.Bottom (_lblAction) + 5
-				};
-				var _ckbSubMenu = new CheckBox ("Has sub-menus") {
-					X = Pos.Left (_lblTitle),
-					Y = Pos.Bottom (_ckbIsTopLevel),
-					Checked = menuItem == null
-				};
-				_ckbIsTopLevel.Toggled += (e) => {
-					if (_ckbIsTopLevel.Checked && menuItem != null) {
-						MessageBox.ErrorQuery ("Invalid IsTopLevel", "Only menu bar can have top level menu item!", "Ok");
-						_ckbIsTopLevel.Checked = false;
-						return;
-					}
-					if (_ckbIsTopLevel.Checked) {
-						_ckbSubMenu.Checked = false;
+			var _btnShortcut = new Button ("Clear Shortcut") {
+				X = Pos.X (_lblShortcut),
+				Y = Pos.Bottom (_txtShortcut) + 1
+			};
+			_btnShortcut.Clicked += () => {
+				_txtShortcut.Text = "";
+			};
+			Add (_btnShortcut);
+
+			_ckbIsTopLevel.Toggled += (e) => {
+				if ((_menuItem != null && _menuItem.Parent != null && _ckbIsTopLevel.Checked) ||
+					_menuItem == null && hasParent && _ckbIsTopLevel.Checked) {
+					MessageBox.ErrorQuery ("Invalid IsTopLevel", "Only menu bar can have top level menu item!", "Ok");
+					_ckbIsTopLevel.Checked = false;
+					return;
+				}
+				if (_ckbIsTopLevel.Checked) {
+					_ckbSubMenu.Checked = false;
+					_ckbSubMenu.SetNeedsDisplay ();
+					_txtHelp.CanFocus = true;
+					_txtAction.CanFocus = true;
+					_txtShortcut.CanFocus = !_ckbIsTopLevel.Checked && !_ckbSubMenu.Checked;
+				} else {
+					if (_menuItem == null && !hasParent || _menuItem.Parent == null) {
+						_ckbSubMenu.Checked = true;
 						_ckbSubMenu.SetNeedsDisplay ();
-						_txtAction.ReadOnly = false;
-					} else {
-						_txtAction.ReadOnly = true;
+						_txtShortcut.CanFocus = false;
 					}
-				};
-				_ckbSubMenu.Toggled += (e) => {
-					if (_ckbSubMenu.Checked) {
-						_ckbIsTopLevel.Checked = false;
+					_txtHelp.Text = "";
+					_txtHelp.CanFocus = false;
+					_txtAction.Text = "";
+					_txtAction.CanFocus = false;
+				}
+			};
+			_ckbSubMenu.Toggled += (e) => {
+				if (_ckbSubMenu.Checked) {
+					_ckbIsTopLevel.Checked = false;
+					_ckbIsTopLevel.SetNeedsDisplay ();
+					_txtHelp.Text = "";
+					_txtHelp.CanFocus = false;
+					_txtAction.Text = "";
+					_txtAction.CanFocus = false;
+					_txtShortcut.Text = "";
+					_txtShortcut.CanFocus = false;
+				} else {
+					if (!hasParent) {
+						_ckbIsTopLevel.Checked = true;
 						_ckbIsTopLevel.SetNeedsDisplay ();
-						_txtAction.ReadOnly = true;
-					} else {
-						_txtAction.ReadOnly = false;
-					}
-				};
-				var _rChkLabels = new ustring [] { "NoCheck", "Checked", "Radio" };
-				var _rbChkStyle = new RadioGroup (_rChkLabels) {
-					X = Pos.Left (_lblTitle),
-					Y = Pos.Bottom (_ckbSubMenu) + 1,
-				};
-				var _btnOk = new Button ("Ok") {
-					IsDefault = true,
-				};
-				_btnOk.Clicked += () => {
-					if (ustring.IsNullOrEmpty (_txtTitle.Text)) {
-						MessageBox.ErrorQuery ("Invalid title", "Must enter a valid title!.", "Ok");
-					} else {
-						Application.RequestStop ();
+						_txtShortcut.CanFocus = false;
 					}
-				};
-				var _btnCancel = new Button ("Cancel");
-				_btnCancel.Clicked += () => {
-					_txtTitle.Text = ustring.Empty;
-					Application.RequestStop ();
-				};
-				var _dialog = new Dialog ("Please enter the menu details.", _btnOk, _btnCancel);
-				_dialog.Add (_lblTitle, _txtTitle, _lblHelp, _txtHelp, _lblAction, _txtAction, _ckbIsTopLevel, _ckbSubMenu, _rbChkStyle);
-				_txtTitle.SetFocus ();
-				Application.Run (_dialog);
+					_txtHelp.CanFocus = true;
+					_txtAction.CanFocus = true;
+					_txtShortcut.CanFocus = !_ckbIsTopLevel.Checked && !_ckbSubMenu.Checked;
+				}
+			};
+
+			//Add (_frmMenuDetails);
+
+		}
 
-				return new DynamicMenuItem (_txtTitle.Text, _txtHelp.Text, _txtAction.Text, _ckbIsTopLevel != null ? _ckbIsTopLevel.Checked : false, _ckbSubMenu != null ? _ckbSubMenu.Checked : false, _rbChkStyle.SelectedItem == 0 ? MenuItemCheckStyle.NoCheck : _rbChkStyle.SelectedItem == 1 ? MenuItemCheckStyle.Checked : MenuItemCheckStyle.Radio);
+
+		public DynamicMenuItem EnterMenuItem ()
+		{
+			var valid = false;
+
+			if (_menuItem == null) {
+				var m = new DynamicMenuItem ();
+				_txtTitle.Text = m.title;
+				_txtHelp.Text = m.help;
+				_txtAction.Text = m.action;
+				_ckbIsTopLevel.Checked = false;
+				_ckbSubMenu.Checked = !hasParent;
+				_txtHelp.CanFocus = hasParent;
+				_txtAction.CanFocus = hasParent;
+				_txtShortcut.CanFocus = hasParent;
+			} else {
+				EditMenuBarItem (_menuItem);
 			}
 
-			MenuItem CreateNewMenu (DynamicMenuItem item, MenuItem parent = null)
-			{
-				MenuItem newMenu;
-				if (item.hasSubMenu) {
-					newMenu = new MenuBarItem (item.title, new MenuItem [] { new MenuItem ("_New", "", null) }, parent);
-					((MenuBarItem)newMenu).Children [0].Action = CreateAction (newMenu, new DynamicMenuItem ());
-				} else if (parent != null) {
-					newMenu = new MenuItem (item.title, item.help, null, null, parent);
-					newMenu.CheckType = item.checkStyle;
-					newMenu.Action = CreateAction (newMenu, item);
+			var _btnOk = new Button ("Ok") {
+				IsDefault = true,
+			};
+			_btnOk.Clicked += () => {
+				if (ustring.IsNullOrEmpty (_txtTitle.Text)) {
+					MessageBox.ErrorQuery ("Invalid title", "Must enter a valid title!.", "Ok");
 				} else {
-					newMenu = new MenuBarItem (item.title, item.help, null);
-					((MenuBarItem)newMenu).Children [0].Action = CreateAction (newMenu, item);
+					valid = true;
+					Application.RequestStop ();
 				}
+			};
+			var _btnCancel = new Button ("Cancel");
+			_btnCancel.Clicked += () => {
+				_txtTitle.Text = ustring.Empty;
+				Application.RequestStop ();
+			};
+			var _dialog = new Dialog ("Please enter the menu details.", _btnOk, _btnCancel);
+
+			Width = Dim.Fill ();
+			Height = Dim.Fill () - 1;
+			_dialog.Add (this);
+			_txtTitle.SetFocus ();
+			_txtTitle.CursorPosition = _txtTitle.Text.Length;
+			Application.Run (_dialog);
+
+			if (valid) {
+				return new DynamicMenuItem (_txtTitle.Text, _txtHelp.Text, _txtAction.Text,
+					_ckbIsTopLevel != null ? _ckbIsTopLevel.Checked : false,
+					_ckbSubMenu != null ? _ckbSubMenu.Checked : false,
+					_rbChkStyle.SelectedItem == 0 ? MenuItemCheckStyle.NoCheck :
+					_rbChkStyle.SelectedItem == 1 ? MenuItemCheckStyle.Checked : MenuItemCheckStyle.Radio,
+					_txtShortcut.Text);
+			} else {
+				return null;
+			}
+		}
 
-				return newMenu;
+		public void EditMenuBarItem (MenuItem menuItem)
+		{
+			if (menuItem == null) {
+				hasParent = false;
+				CanFocus = false;
+				CleanEditMenuBarItem ();
+				return;
+			} else {
+				hasParent = menuItem.Parent != null;
+				CanFocus = true;
+			}
+			_menuItem = menuItem;
+			_txtTitle.Text = menuItem?.Title ?? "";
+			_txtHelp.Text = menuItem?.Help ?? "";
+			_txtAction.Text = menuItem != null && menuItem.Action != null ? GetTargetAction (menuItem.Action) : ustring.Empty;
+			_ckbIsTopLevel.Checked = IsTopLevel (menuItem);
+			_ckbSubMenu.Checked = HasSubMenus (menuItem);
+			_txtHelp.CanFocus = !_ckbSubMenu.Checked;
+			_txtAction.CanFocus = !_ckbSubMenu.Checked;
+			_rbChkStyle.SelectedItem = (int)(menuItem?.CheckType ?? MenuItemCheckStyle.NoCheck);
+			_txtShortcut.Text = menuItem?.ShortcutTag ?? "";
+			_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;
+
+			if (me == null) {
+				throw new ArgumentException ();
+			}
+			object v = new object ();
+			foreach (var field in me.GetType ().GetFields ()) {
+				if (field.Name == "item") {
+					v = field.GetValue (me);
+				}
+			}
+			return v == null || !(v is DynamicMenuItem item) ? ustring.Empty : item.action;
+		}
+
+		bool IsTopLevel (MenuItem menuItem)
+		{
+			var topLevel = menuItem as MenuBarItem;
+			if (topLevel != null && topLevel.Parent == null && (topLevel.Children == null || topLevel.Children.Length == 0) && topLevel.Action != null) {
+				return true;
+			} else {
+				return false;
+			}
+		}
+
+		bool HasSubMenus (MenuItem menuItem)
+		{
+			var menuBarItem = menuItem as MenuBarItem;
+			if (menuBarItem != null && menuBarItem.Children != null && (menuBarItem.Children.Length > 0 || menuBarItem.Action == null)) {
+				return true;
+			} else {
+				return false;
+			}
+		}
+
+		public Action CreateAction (MenuItem menuItem, DynamicMenuItem item)
+		{
+			switch (menuItem.CheckType) {
+			case MenuItemCheckStyle.NoCheck:
+				return new Action (() => MessageBox.ErrorQuery (item.title, item.action, "Ok"));
+			case MenuItemCheckStyle.Checked:
+				return new Action (() => menuItem.Checked = !menuItem.Checked);
+			case MenuItemCheckStyle.Radio:
+				break;
+			}
+			return new Action (() => {
+				menuItem.Checked = true;
+				var parent = menuItem?.Parent as MenuBarItem;
+				if (parent != null) {
+					var childrens = parent.Children;
+					for (int i = 0; i < childrens.Length; i++) {
+						var child = childrens [i];
+						if (child != menuItem) {
+							child.Checked = false;
+						}
+					}
+				}
+			});
+		}
+
+		public void UpdateParent (ref MenuItem menuItem)
+		{
+			var parent = menuItem.Parent as MenuBarItem;
+			var idx = parent.GetChildrenIndex (menuItem);
+			if (!(menuItem is MenuBarItem)) {
+				menuItem = new MenuBarItem (menuItem.Title, new MenuItem [] { }, menuItem.Parent);
+				if (idx > -1) {
+					parent.Children [idx] = menuItem;
+				}
+			} else {
+				menuItem = new MenuItem (menuItem.Title, menuItem.Help, CreateAction (menuItem, new DynamicMenuItem ()), null, menuItem.Parent);
+				if (idx > -1) {
+					parent.Children [idx] = menuItem;
+				}
 			}
 		}
 	}
 
-	class DynamicMenuItemModel : INotifyPropertyChanged {
+	public class DynamicMenuItemModel : INotifyPropertyChanged {
 		public event PropertyChangedEventHandler PropertyChanged;
 
 		private ustring menuBar;

+ 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),
 			});

+ 44 - 11
UICatalog/UICatalog.cs

@@ -6,6 +6,7 @@ using System.Diagnostics;
 using System.Globalization;
 using System.Linq;
 using System.Reflection;
+using System.Runtime.InteropServices;
 using System.Text;
 using Terminal.Gui;
 using Rune = System.Rune;
@@ -104,7 +105,7 @@ namespace UICatalog {
 					_rightPane.SetFocus ();
 					_top.Ready -= ReadyHandler;
 				}
-				
+
 				_top.Ready += ReadyHandler;
 
 #if DEBUG_IDISPOSABLE
@@ -113,7 +114,7 @@ namespace UICatalog {
 				foreach (var inst in Responder.Instances) {
 					Debug.Assert (inst.WasDisposed);
 				}
-				Responder.Instances.Clear();
+				Responder.Instances.Clear ();
 #endif
 			}
 
@@ -156,11 +157,15 @@ namespace UICatalog {
 
 			_menu = new MenuBar (new MenuBarItem [] {
 				new MenuBarItem ("_File", new MenuItem [] {
-					new MenuItem ("_Quit", "", () => Application.RequestStop() )
+					new MenuItem ("_Quit", "", () => Application.RequestStop(), null, null, Key.Q | Key.CtrlMask)
 				}),
 				new MenuBarItem ("_Color Scheme", CreateColorSchemeMenuItems()),
-				new MenuBarItem ("_Diagostics", CreateDiagnosticMenuItems()),
-				new MenuBarItem ("_About...", "About this app", () =>  MessageBox.Query ("About UI Catalog", aboutMessage.ToString(), "_Ok")),
+				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.CtrlMask | Key.A),
+				})
 			});
 
 			_leftPane = new FrameView ("Categories") {
@@ -169,8 +174,10 @@ namespace UICatalog {
 				Width = 25,
 				Height = Dim.Fill (1),
 				CanFocus = false,
+				Shortcut = Key.CtrlMask | Key.C
 			};
-
+			_leftPane.Title = $"{_leftPane.Title} ({_leftPane.ShortcutTag})";
+			_leftPane.ShortcutAction = () => _leftPane.SetFocus ();
 
 			_categories = Scenario.GetAllCategories ().OrderBy (c => c).ToList ();
 			_categoryListView = new ListView (_categories) {
@@ -193,8 +200,10 @@ namespace UICatalog {
 				Width = Dim.Fill (),
 				Height = Dim.Fill (1),
 				CanFocus = true,
-
+				Shortcut = Key.CtrlMask | Key.S
 			};
+			_rightPane.Title = $"{_rightPane.Title} ({_rightPane.ShortcutTag})";
+			_rightPane.ShortcutAction = () => _rightPane.SetFocus ();
 
 			_nameColumnWidth = Scenario.ScenarioMetadata.GetName (_scenarios.OrderByDescending (t => Scenario.ScenarioMetadata.GetName (t).Length).FirstOrDefault ()).Length;
 
@@ -217,14 +226,14 @@ namespace UICatalog {
 			_numlock = new StatusItem (Key.CharMask, "Num", null);
 			_scrolllock = new StatusItem (Key.CharMask, "Scroll", null);
 
-			_statusBar = new StatusBar () { 
+			_statusBar = new StatusBar () {
 				Visible = true,
 			};
 			_statusBar.Items = new StatusItem [] {
 				_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;
@@ -261,10 +270,14 @@ namespace UICatalog {
 
 		static MenuItem [] CreateDiagnosticMenuItems ()
 		{
+			var index = 0;
+
 			MenuItem CheckedMenuMenuItem (ustring menuItem, Action action, Func<bool> checkFunction)
 			{
 				var mi = new MenuItem ();
 				mi.Title = menuItem;
+				mi.Shortcut = Key.AltMask + index.ToString () [0];
+				index++;
 				mi.CheckType |= MenuItemCheckStyle.Checked;
 				mi.Checked = checkFunction ();
 				mi.Action = () => {
@@ -309,14 +322,15 @@ namespace UICatalog {
 			List<MenuItem> menuItems = new List<MenuItem> ();
 			foreach (var sc in Colors.ColorSchemes) {
 				var item = new MenuItem ();
-				item.Title = sc.Key;
+				item.Title = $"_{sc.Key}";
+				item.Shortcut = Key.AltMask | (Key)sc.Key.Substring (0, 1) [0];
 				item.CheckType |= MenuItemCheckStyle.Radio;
 				item.Checked = sc.Value == _baseColorScheme;
 				item.Action += () => {
 					_baseColorScheme = sc.Value;
 					SetColorScheme ();
 					foreach (var menuItem in menuItems) {
-						menuItem.Checked = menuItem.Title.Equals (sc.Key) && sc.Value == _baseColorScheme;
+						menuItem.Checked = menuItem.Title.Equals ($"_{sc.Key}") && sc.Value == _baseColorScheme;
 					}
 				};
 				menuItems.Add (item);
@@ -440,5 +454,24 @@ namespace UICatalog {
 			_scenarioListView.SelectedItem = _scenarioListViewItem;
 
 		}
+
+		private static void OpenUrl (string url)
+		{
+			try {
+				Process.Start (url);
+			} catch {
+				// hack because of this: https://github.com/dotnet/corefx/issues/10361
+				if (RuntimeInformation.IsOSPlatform (OSPlatform.Windows)) {
+					url = url.Replace ("&", "^&");
+					Process.Start (new ProcessStartInfo ("cmd", $"/c start {url}") { CreateNoWindow = true });
+				} else if (RuntimeInformation.IsOSPlatform (OSPlatform.Linux)) {
+					Process.Start ("xdg-open", url);
+				} else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) {
+					Process.Start ("open", url);
+				} else {
+					throw;
+				}
+			}
+		}
 	}
 }

+ 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);