浏览代码

Recover from rebase.

BDisp 4 年之前
父节点
当前提交
21650576eb

+ 26 - 30
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.ControlC),
+				new MenuItem ("C_ut", "", null, null, null, Key.ControlX),
+				new MenuItem ("_Paste", "", null, null, null, Key.ControlV)
 			}),
 		});
 		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.ControlD),
+				new MenuItem ("_New", "Creates new file", NewFile, null, null, Key.AltMask | Key.ControlN),
+				new MenuItem ("_Open", "", Open, null, null, Key.AltMask | Key.ControlO),
+				new MenuItem ("_Hex", "", () => { running = ShowHex; Application.RequestStop (); }, null, null, Key.AltMask | Key.ControlH),
+				new MenuItem ("_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.ControlQ)
 			}),
 			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.ControlC),
+				new MenuItem ("C_ut", "", Cut, null, null, Key.AltMask | Key.ControlX),
+				new MenuItem ("_Paste", "", Paste, null, null, Key.AltMask | Key.ControlV),
 				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.ControlG),
+				new MenuItem ("_OnKeyDown/Press/Up", "", () => OnKeyDownPressUpDemo (), null, null, Key.AltMask | Key.ControlK)
 			}),
 			new MenuBarItem ("_Test Menu and SubMenus", new MenuBarItem [] {
 				new MenuBarItem ("SubMenu1Item_1",  new MenuBarItem [] {

+ 80 - 10
Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs

@@ -439,11 +439,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;
@@ -472,8 +472,23 @@ 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);
+				}
+				keyDownHandler (new KeyEvent (k, MapKeyModifiers (k)));
+				keyHandler (new KeyEvent (k, MapKeyModifiers (k)));
+				keyUpHandler (new KeyEvent (k, MapKeyModifiers (k)));
 				return;
 			}
 
@@ -498,19 +513,65 @@ namespace Terminal.Gui {
 					} else if (wch2 - (int)Key.Space >= 'A' && wch2 - (int)Key.Space <= '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));
+					} else if (wch2 >= (uint)Key.ControlA && wch2 <= (uint)Key.ControlZ) {
+						k = (Key)((uint)(Key.AltMask | Key.CtrlMask) + wch2);
 						key = new KeyEvent (k, MapKeyModifiers (k));
-					} else if (wch2 == '0') {
-						k = Key.F10;
+					} else if (wch2 >= '0' && wch2 <= '9') {
+						k = (Key)((uint)Key.AltMask + (uint)Key.D0 + (wch2 - '0'));
 						key = new KeyEvent (k, MapKeyModifiers (k));
 					} else if (wch2 == 27) {
 						k = (Key)wch2;
 						key = new KeyEvent (k, MapKeyModifiers (k));
+					} else if (wch2 == 91) {
+						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) { // Ctrl+Alt+(F1 - F4)
+							wch2 = c [3] + 185;
+							k = Key.CtrlMask | Key.AltMask | MapCursesKey (wch2);
+						} else if (c [0] == 49 && c [2] == 59 && c [3] == 55 && c [4] == 126) { // Ctrl+Alt+(F5 - F8)
+							wch2 = c [1] == 53 ? c [1] + 216 : c [1] + 215;
+							k = Key.CtrlMask | Key.AltMask | MapCursesKey (wch2);
+						} else if (c [0] == 50 && c [2] == 59 && c [3] == 55 && c [4] == 126) { // Ctrl+Alt+(F9 - F12)
+							wch2 = c [1] < 51 ? c [1] + 225 : c [1] + 224;
+							k = Key.CtrlMask | Key.AltMask | MapCursesKey (wch2);
+						} else if (c [0] == 49 && c [1] == 59 && c [2] == 56) { // Ctrl+Shift+Alt+(F1 - F4)
+							wch2 = c [3] + 185;
+							k = Key.CtrlMask | Key.ShiftMask | Key.AltMask | MapCursesKey (wch2);
+						} else if (c [0] == 49 && c [2] == 59 && c [3] == 56 && c [4] == 126) { // Ctrl+Shift+Alt+(F5 - F8)
+							wch2 = c [1] == 53 ? c [1] + 216 : c [1] + 215;
+							k = Key.CtrlMask | Key.ShiftMask | Key.AltMask | MapCursesKey (wch2);
+						} else if (c [0] == 50 && c [2] == 59 && c [3] == 56 && c [4] == 126) {  // Ctrl+Shift+Alt+(F9 - F12)
+							wch2 = c [1] < 51 ? c [1] + 225 : c [1] + 224;
+							k = Key.CtrlMask | Key.ShiftMask | Key.AltMask | MapCursesKey (wch2);
+						} else {
+							k = MapCursesKey (wch2);
+						}
+						key = new KeyEvent (k, MapKeyModifiers (k));
 					} else {
-						k = Key.AltMask | (Key)wch2;
+						// Unfortunately there are no way to differentiate Ctrl+Alt+alfa and Ctrl+Shift+Alt+alfa.
+						if (wch2 == 0) {
+							k = Key.CtrlMask | Key.AltMask | Key.Space;
+						} else {
+							if (((Key)wch2).ToString ().Contains ("Control")) {
+								keyModifiers.Ctrl = true;
+							}
+							if (wch2 < 256) {
+								k = Key.AltMask | (Key)wch2;
+							} else {
+								//k = (Key)wch2;
+								//keyModifiers.Alt = true;
+								k = (Key)((uint)(Key.AltMask | Key.CtrlMask) + wch2);
+							}
+						}
 						key = new KeyEvent (k, MapKeyModifiers (k));
 					}
+					keyDownHandler (key);
 					keyHandler (key);
 				} else {
 					k = Key.Esc;
@@ -521,7 +582,16 @@ 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.ControlA && wch <= (uint)Key.ControlZ) {
+					k = (Key)wch;
+					keyModifiers.Ctrl = true;
+				} else if (wch >= 'A' && wch <= '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 ();

+ 23 - 11
Terminal.Gui/ConsoleDrivers/WindowsDriver.cs

@@ -1118,30 +1118,42 @@ namespace Terminal.Gui {
 
 			if (key >= ConsoleKey.A && key <= ConsoleKey.Z) {
 				var delta = key - ConsoleKey.A;
-				if (keyInfo.Modifiers == ConsoleModifiers.Control)
+				if (keyInfo.Modifiers == ConsoleModifiers.Control) {
 					return (Key)((uint)Key.ControlA + delta);
-				if (keyInfo.Modifiers == ConsoleModifiers.Alt)
+				}
+				if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
 					return (Key)(((uint)Key.AltMask) | ((uint)'A' + delta));
+				}
 				if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
-					if (keyInfo.KeyChar == 0)
-						return (Key)(((uint)Key.AltMask) + ((uint)Key.ControlA + delta));
-					else
+					if (keyInfo.KeyChar == 0) {
+						return (Key)(((uint)Key.AltMask | (uint)Key.CtrlMask) | ((uint)Key.ControlA + delta));
+					} else {
 						return (Key)((uint)keyInfo.KeyChar);
+					}
 				}
 				//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)
+				if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
 					return (Key)(((uint)Key.AltMask) | ((uint)'0' + delta));
-
+				}
+				if (keyInfo.Modifiers == ConsoleModifiers.Control) {
+					return (Key)(((uint)Key.CtrlMask) | ((uint)'0' + delta));
+				}
+				if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30) {
+					return MapKeyModifiers (keyInfo, (Key)((uint)'0' + 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,11 +1165,11 @@ 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;

+ 40 - 0
Terminal.Gui/Core/Event.cs

@@ -318,6 +318,46 @@ namespace Terminal.Gui {
 		/// </summary>
 		BackTab,
 		/// <summary>
+		/// Digit 0.
+		/// </summary>
+		D0 = 48,
+		/// <summary>
+		/// Digit 1.
+		/// </summary>
+		D1,
+		/// <summary>
+		/// Digit 2.
+		/// </summary>
+		D2,
+		/// <summary>
+		/// Digit 3.
+		/// </summary>
+		D3,
+		/// <summary>
+		/// Digit 4.
+		/// </summary>
+		D4,
+		/// <summary>
+		/// Digit 5.
+		/// </summary>
+		D5,
+		/// <summary>
+		/// Digit 6.
+		/// </summary>
+		D6,
+		/// <summary>
+		/// Digit 7.
+		/// </summary>
+		D7,
+		/// <summary>
+		/// Digit 8.
+		/// </summary>
+		D8,
+		/// <summary>
+		/// Digit 9.
+		/// </summary>
+		D9,
+		/// <summary>
 		/// A key with an unknown mapping was raised.
 		/// </summary>
 		Unknown

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

@@ -2055,5 +2055,26 @@ namespace Terminal.Gui {
 
 			return true;
 		}
+
+		/// <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 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;
+		}
 	}
 }

+ 262 - 29
Terminal.Gui/Views/Menu.cs

@@ -40,14 +40,19 @@ 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;
+		Key shortCut;
 
 		/// <summary>
 		/// Initializes a new instance of <see cref="MenuItem"/>
 		/// </summary>
-		public MenuItem ()
+		public MenuItem (Key shortCut = Key.ControlSpace)
 		{
 			Title = "";
 			Help = "";
+			if (shortCut != Key.ControlSpace) {
+				ShortCut = shortCut;
+			}
 		}
 
 		/// <summary>
@@ -57,26 +62,18 @@ 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.ControlSpace)
 		{
 			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;
+			if (shortCut != Key.ControlSpace) {
+				ShortCut = shortCut;
+			}
 		}
 
 		/// <summary>
@@ -86,11 +83,41 @@ namespace Terminal.Gui {
 		/// </summary>
 		public Rune HotKey;
 
+		/// <summary>
+		/// This is the global setting that can be used as a global shortcut to invoke the action on the menu.
+		/// </summary>
+		public Key ShortCut {
+			get => shortCut;
+			set {
+				if (shortCut != value) {
+					if (GetKeyToString (value).Contains ("Control")) {
+						shortCut = Key.CtrlMask | value;
+					} else {
+						shortCut = value;
+					}
+					ShortCutTag = GetShortCutTag (shortCut);
+				}
+			}
+		}
+
+		/// <summary>
+		/// The keystroke combination used in the <see cref="ShortCut"/> as string.
+		/// </summary>
+		public ustring ShortCutTag { get; private set; } = ustring.Empty;
+
 		/// <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 +146,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 +185,154 @@ 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>
+		/// Get the <see cref="ShortCut"/> key as string.
+		/// </summary>
+		/// <param name="shortCut">The shortcut key.</param>
+		/// <returns></returns>
+		public ustring GetShortCutTag (Key shortCut)
+		{
+			if (shortCut == Key.ControlSpace) {
+				return "";
+			}
+
+			var k = (uint)shortCut;
+			var delimiter = MenuBar.ShortCutDelimiter;
+			ustring tag = ustring.Empty;
+			var sCut = GetKeyToString (shortCut).ToString ();
+			if (sCut.Contains ("Control") || (shortCut & Key.CtrlMask) != 0) {
+				tag = "Ctrl";
+			}
+			if ((shortCut & Key.ShiftMask) != 0) {
+				if (!tag.IsEmpty) {
+					tag += delimiter;
+				}
+				tag += "Shift";
+			}
+			if ((shortCut & Key.AltMask) != 0) {
+				if (!tag.IsEmpty) {
+					tag += delimiter;
+				}
+				tag += "Alt";
+			}
+
+			ustring [] keys = ustring.Make (sCut).Split (",");
+			for (int i = 0; i < keys.Length; i++) {
+				var key = keys [i].TrimSpace ();
+				if (key == Key.AltMask.ToString () || key == Key.ShiftMask.ToString () || key == Key.CtrlMask.ToString ()) {
+					continue;
+				}
+				if (!tag.IsEmpty) {
+					tag += delimiter;
+				}
+				if (key.Contains ("Control")) {
+					tag += ((char)key.ElementAt (7)).ToString ();
+				} else if (!key.Contains ("F") && key.Length > 2 && keys.Length == 1) {
+					k = (uint)Key.AltMask + k;
+					tag += ((char)k).ToString ();
+				} else if (key.Length == 2 && key.StartsWith ("D")) {
+					tag += ((char)key.ElementAt (1)).ToString ();
+				} else {
+					tag += key;
+				}
+			}
+
+			return tag;
+		}
+
+		ustring GetKeyToString (Key key)
+		{
+			if (key == Key.ControlSpace) {
+				return "";
+			}
+
+			var mK = key & (Key.AltMask | Key.CtrlMask | Key.ShiftMask);
+			for (uint i = (uint)Key.F1; i < (uint)Key.F12; i++) {
+				if ((key | (Key)i) == key) {
+					mK |= (Key)i;
+				}
+			}
+			var k = key;
+			k &= ~mK;
+			int.TryParse (k.ToString (), out int c);
+			var s = mK == Key.ControlSpace ? "" : mK.ToString ();
+			if (s != "" && (k != Key.ControlSpace || c > 0)) {
+				s += ",";
+			}
+			s += c == 0 ? k == Key.ControlSpace ? "" : k.ToString () : ((char)c).ToString ();
+			return s;
+		}
+
+		/// <summary>
+		/// Allows to generate a <see cref="Key"/> from a <see cref="ShortCutTag"/>
+		/// </summary>
+		/// <param name="tag">The key as string.</param>
+		/// <returns></returns>
+		public Key CreateShortCutFromTag (ustring tag)
+		{
+			var sCut = tag;
+			if (sCut.IsEmpty) {
+				return default;
+			}
+
+			Key key = Key.ControlSpace;
+			var hasCtrl = false;
+			var delimiter = MenuBar.ShortCutDelimiter;
+
+			ustring [] keys = sCut.Split (MenuBar.ShortCutDelimiter);
+			for (int i = 0; i < keys.Length; i++) {
+				var k = keys [i];
+				if (k == "Ctrl") {
+					hasCtrl = true;
+					key |= Key.CtrlMask;
+				} else if (k == "Shift") {
+					key |= Key.ShiftMask;
+				} else if (k == "Alt") {
+					key |= Key.AltMask;
+				} else if (k.StartsWith ("F") && k.Length > 1) {
+					int.TryParse (k.Substring (1).ToString (), out int n);
+					for (uint j = (uint)Key.F1; j < (uint)Key.F12; j++) {
+						int.TryParse (((Key)j).ToString ().Substring (1), out int f);
+						if (f == n) {
+							key |= (Key)j;
+						}
+					}
+				} else if (k [0] >= 'A' && k [0] <= 'Z') {
+					if (hasCtrl) {
+						var n = k [0] - 'A' + 1;
+						var d = n - (uint)Key.ControlA;
+						key |= (Key)((uint)Key.ControlA + d);
+					} else {
+						key |= (Key)k [0];
+					}
+				} else if (k [0] >= '0' && k [0] <= '9') {
+					//var n = k [0] - (uint)Key.D0 + 1;
+					//var d = n - (uint)Key.D0;
+					//key |= (Key)((uint)Key.D0 + d);
+					key |= (Key)k [0];
+				}
+			}
+
+			return key;
+		}
 	}
 
 	/// <summary>
@@ -307,7 +483,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; }
 
 	}
 
@@ -416,9 +592,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 +887,11 @@ namespace Terminal.Gui {
 		/// </summary>
 		public bool UseKeysUpDownAsKeysLeftRight { get; set; } = true;
 
+		/// <summary>
+		/// Used for change the shortcut delimiter separator.
+		/// </summary>
+		public static ustring ShortCutDelimiter { get; set; } = "+";
+
 		/// <summary>
 		/// Initializes a new instance of the <see cref="MenuBar"/>.
 		/// </summary>
@@ -828,8 +1016,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 +1031,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 +1111,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 +1446,44 @@ namespace Terminal.Gui {
 			return false;
 		}
 
+		internal bool FindAndOpenMenuByShortCut (KeyEvent kb, MenuItem [] children = null)
+		{
+			if (children == null) {
+				children = Menus;
+			}
+
+			var key = kb.KeyValue;
+			var keys = GetModifiersKey (kb);
+			key |= (int)keys;
+			//if (kb.IsShift) {
+			//	key |= (int)Key.ShiftMask;
+			//}
+			//if (kb.IsAlt) {
+			//	key |= unchecked((int)Key.AltMask);
+			//}
+			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.ControlSpace && 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 +1519,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;
@@ -1358,6 +1584,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 +1604,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 +1656,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)) {

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

@@ -345,9 +345,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 (GetModifiersKey (kb)) {
 			case Key.DeleteChar:
-			case Key.ControlD:
+			case Key.ControlD | Key.CtrlMask:
 				if (ReadOnly)
 					return true;
 
@@ -398,7 +398,7 @@ namespace Terminal.Gui {
 
 			// Home, C-A
 			case Key.Home:
-			case Key.ControlA:
+			case Key.ControlA | Key.CtrlMask:
 				ClearAllSelection ();
 				point = 0;
 				Adjust ();
@@ -441,7 +441,7 @@ namespace Terminal.Gui {
 				break;
 
 			case Key.CursorLeft:
-			case Key.ControlB:
+			case Key.ControlB | Key.CtrlMask:
 				ClearAllSelection ();
 				if (point > 0) {
 					point--;
@@ -450,14 +450,14 @@ namespace Terminal.Gui {
 				break;
 
 			case Key.End:
-			case Key.ControlE: // End
+			case Key.ControlE | Key.CtrlMask: // End
 				ClearAllSelection ();
 				point = text.Count;
 				Adjust ();
 				break;
 
 			case Key.CursorRight:
-			case Key.ControlF:
+			case Key.ControlF | Key.CtrlMask:
 				ClearAllSelection ();
 				if (point == text.Count)
 					break;
@@ -465,7 +465,7 @@ namespace Terminal.Gui {
 				Adjust ();
 				break;
 
-			case Key.ControlK: // kill-to-end
+			case Key.ControlK | Key.CtrlMask: // kill-to-end
 				if (ReadOnly)
 					return true;
 
@@ -478,7 +478,7 @@ namespace Terminal.Gui {
 				break;
 
 			// Undo
-			case Key.ControlZ:
+			case Key.ControlZ | Key.CtrlMask:
 				if (ReadOnly)
 					return true;
 
@@ -494,7 +494,7 @@ namespace Terminal.Gui {
 				break;
 
 			//Redo
-			case Key.ControlY: // Control-y, yank
+			case Key.ControlY | Key.CtrlMask: // Control-y, yank
 				if (ReadOnly)
 					return true;
 
@@ -552,18 +552,18 @@ namespace Terminal.Gui {
 				SetNeedsDisplay ();
 				break;
 
-			case Key.ControlC:
+			case Key.ControlC | Key.CtrlMask:
 				Copy ();
 				break;
 
-			case Key.ControlX:
+			case Key.ControlX | Key.CtrlMask:
 				if (ReadOnly)
 					return true;
 
 				Cut ();
 				break;
 
-			case Key.ControlV:
+			case Key.ControlV | Key.CtrlMask:
 				Paste ();
 				break;
 

+ 529 - 370
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 ("Delimiter") {
+				X = Pos.Center (),
+				Y = 3,
+				Width = 20,
+				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,124 +198,13 @@ 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 ()
-			};
-
-			var _lblTitle = new Label ("Title:") {
-				Y = 1
-			};
-			_frmMenuDetails.Add (_lblTitle);
-
-			var _txtTitle = new TextField () {
-				X = Pos.Right (_lblTitle) + 2,
-				Y = Pos.Top (_lblTitle),
-				Width = Dim.Fill ()
-			};
-			_frmMenuDetails.Add (_txtTitle);
-
-			var _lblHelp = new Label ("Help:") {
-				X = Pos.Left (_lblTitle),
-				Y = Pos.Bottom (_lblTitle) + 1
-			};
-			_frmMenuDetails.Add (_lblHelp);
-
-			var _txtHelp = new TextField () {
-				X = Pos.Left (_txtTitle),
-				Y = Pos.Top (_lblHelp),
-				Width = Dim.Fill ()
-			};
-			_frmMenuDetails.Add (_txtHelp);
-
-			var _lblAction = new Label ("Action:") {
-				X = Pos.Left (_lblTitle),
-				Y = Pos.Bottom (_lblHelp) + 1
-			};
-			_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
-			};
-			_frmMenuDetails.Add (_ckbIsTopLevel);
-
-			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;
-				}
-			};
-			_ckbSubMenu.Toggled += (e) => {
-				if (_ckbSubMenu.Checked) {
-					_ckbIsTopLevel.Checked = false;
-					_ckbIsTopLevel.SetNeedsDisplay ();
-					_txtAction.ReadOnly = true;
-				} else {
-					_txtAction.ReadOnly = false;
-				}
+				Height = Dim.Fill (2)
 			};
-
-			var _rChkLabels = new ustring [] { "NoCheck", "Checked", "Radio" };
-			var _rbChkStyle = new RadioGroup (_rChkLabels) {
-				X = Pos.Left (_lblTitle),
-				Y = Pos.Bottom (_ckbSubMenu) + 1,
-			};
-			_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) {
-					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);
-					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 += () => {
@@ -305,15 +214,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,14 +260,18 @@ 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 ();
 				}
 			};
 
@@ -419,9 +333,105 @@ namespace UICatalog {
 				}
 			};
 
+			_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 _btnOk = new Button ("Ok") {
+				X = Pos.Right (_frmMenu) + 20,
+				Y = Pos.Bottom (_frmMenuDetails),
+			};
+			Add (_btnOk);
+
+			var _btnCancel = new Button ("Cancel") {
+				X = Pos.Right (_btnOk) + 3,
+				Y = Pos.Top (_btnOk),
+			};
+			_btnCancel.Clicked += () => {
+				SetFrameDetails (_currentEditMenuBarItem);
+			};
+			Add (_btnCancel);
+
+			_lstMenus.SelectedItemChanged += (e) => {
+				var menuBarItem = DataContext.Menus.Count > 0 ? DataContext.Menus [e.Item].MenuItem : null;
+				SetFrameDetails (menuBarItem);
+			};
+
+			_btnOk.Clicked += () => {
+				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 (_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);
+				}
+			};
+
+			_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);
+			};
+
+			_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 ();
+			};
+
+			_btnPrevious.Clicked += () => {
+				if (_currentSelectedMenuBar - 1 > -1) {
+					_currentSelectedMenuBar--;
+				}
+				SelectCurrentMenuBarItem ();
+			};
+
+			_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 ();
-			};
+			SetFrameDetails ();
 
-			_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);
-			};
-
-			_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,29 @@ namespace UICatalog {
 			var lstMenus = new Binding (this, "Menus", _lstMenus, "Source", listWrapperConverter);
 
 
-			ustring GetTargetAction (Action action)
+			void SetFrameDetails (MenuItem menuItem = null)
 			{
-				var me = action.Target;
-
-				if (me == null) {
-					throw new ArgumentException ();
+				_currentEditMenuBarItem = menuItem;
+				_frmMenuDetails.EditMenuBarItem (menuItem);
+				var f = _btnOk.CanFocus == _frmMenuDetails.CanFocus;
+				if (!f) {
+					_btnOk.CanFocus = _frmMenuDetails.CanFocus;
+					_btnCancel.CanFocus = _frmMenuDetails.CanFocus;
 				}
-				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;
 			}
 
-			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 +546,24 @@ 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);
+				} 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);
+				}
+
+				return newMenu;
 			}
 
 			void UpdateMenuItem (MenuItem _currentEditMenuBarItem, DynamicMenuItem menuItem, int index)
@@ -625,199 +577,406 @@ 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 = _currentEditMenuBarItem.CreateShortCutFromTag (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) => {
+				var k = GetModifiersKey (e.KeyEvent);
+				if (_menuItem == null || ((k & (Key.CtrlMask | Key.ShiftMask | Key.AltMask)) == 0 && !CheckFlagRange (k, Key.F1, Key.F12))) {
+					_txtShortCut.Text = "";
+					return;
 				}
-			}
 
-			bool IsTopLevel (MenuItem menuItem)
-			{
-				var topLevel = menuItem as MenuBarItem;
-				if (topLevel != null && topLevel.Parent == null && (topLevel.Children == null || topLevel.Children.Length == 0)) {
-					return true;
+				var s = _menuItem.GetShortCutTag (k);
+				if (s.Contains ("Unknow")) {
+					_txtShortCut.Text = "";
 				} else {
-					return false;
+					_txtShortCut.Text = s;
 				}
-			}
+				e.Handled = true;
+			};
 
-			bool HasSubMenus (MenuItem menuItem)
+			bool CheckFlagRange (Key key, Key first, Key last)
 			{
-				var menuBarItem = menuItem as MenuBarItem;
-				if (menuBarItem != null && menuBarItem.Children != null && menuBarItem.Children.Length > 0) {
-					return true;
-				} else {
-					return false;
+				for (uint i = (uint)first; i < (uint)last; i++) {
+					if ((key | (Key)i) == key) {
+						return true;
+					}
 				}
+				return false;
 			}
 
-			void SelectCurrentMenuBarItem ()
-			{
-				MenuBarItem menuBarItem = null;
-				if (_menuBar?.Menus != null) {
-					menuBarItem = _menuBar.Menus [_currentSelectedMenuBar];
-					_lblMenuBar.Text = menuBarItem.Title;
+			_txtShortCut.KeyUp += (e) => {
+				var k = GetModifiersKey (e.KeyEvent);
+				if (_menuItem == null || (k & (Key.CtrlMask | Key.ShiftMask | Key.AltMask)) == 0 || ((k | Key.D0) == 0 && !CheckFlagRange (k, Key.F1, Key.F12))) {
+					return;
 				}
-				EditMenuBarItem (menuBarItem);
-				_currentMenuBarItem = menuBarItem;
-				DataContext.Menus = new List<DynamicMenuItemList> ();
-				SetListViewSource (_currentMenuBarItem, true);
-				_lblParent.Text = ustring.Empty;
-			}
 
-			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;
+				e.Handled = true;
+				var kVal = (Key)e.KeyEvent.KeyValue;
+				if ((kVal & Key.D0) != 0) {
+
+				} else if ((kVal == Key.CtrlMask || kVal == Key.ShiftMask || kVal == Key.AltMask ||
+				       (kVal & (Key.CtrlMask | Key.ShiftMask)) != 0 || (kVal & (Key.CtrlMask | Key.AltMask)) != 0 || (kVal & (Key.ShiftMask | Key.AltMask)) != 0 ||
+				       (kVal & (Key.CtrlMask | Key.ShiftMask | Key.AltMask)) != 0) && !kVal.ToString ().Contains ("Control")) {
+					return;
+				}
+				var s = _menuItem.GetShortCutTag (k);
+				if (s.Contains ("Unknow")) {
+					_txtShortCut.Text = "";
+				} else {
+					_txtShortCut.Text = s;
+				}
+			};
+			Add (_txtShortCut);
+
+			var _btnShortCut = new Button ("Clear ShirtCut") {
+				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.CanFocus = false;
+				} else {
+					if (!hasParent) {
+						_ckbIsTopLevel.Checked = true;
 						_ckbIsTopLevel.SetNeedsDisplay ();
-						_txtAction.ReadOnly = true;
-					} else {
-						_txtAction.ReadOnly = false;
+						_txtShortCut.CanFocus = 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 ();
-					}
-				};
-				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 ();
+			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;
+				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;
+		}
+
+		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;

+ 34 - 5
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;
@@ -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.ControlQ)
 				}),
 				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.ControlA),
+				})
 			});
 
 			_leftPane = new FrameView ("Categories") {
@@ -261,10 +266,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 +318,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 +450,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;
+				}
+			}
+		}
 	}
 }