Bläddra i källkod

Merge pull request #2117 from tig/fix_2109_menubar_spacing

Fixes #2109. MenuBar has extra space on left. Refactors MenuTests. Better Menu API docs.
Tig 2 år sedan
förälder
incheckning
8cbd5977df

+ 38 - 19
Terminal.Gui/Core/ContextMenu.cs → Terminal.Gui/Views/ContextMenu.cs

@@ -2,8 +2,24 @@
 
 
 namespace Terminal.Gui {
 namespace Terminal.Gui {
 	/// <summary>
 	/// <summary>
-	/// A context menu window derived from <see cref="MenuBar"/> containing menu items
-	/// which can be opened in any position.
+	/// ContextMenu provides a pop-up menu that can be positioned anywhere within a <see cref="View"/>. 
+	/// ContextMenu is analogous to <see cref="MenuBar"/> and, once activated, works like a sub-menu 
+	/// of a <see cref="MenuBarItem"/> (but can be positioned anywhere).
+	/// <para>
+	/// By default, a ContextMenu with sub-menus is displayed in a cascading manner, where each sub-menu pops out of the ContextMenu frame
+	/// (either to the right or left, depending on where the ContextMenu is relative to the edge of the screen). By setting
+	/// <see cref="UseSubMenusSingleFrame"/> to <see langword="true"/>, this behavior can be changed such that all sub-menus are
+	/// drawn within the ContextMenu frame.
+	/// </para>
+	/// <para>
+	/// ContextMenus can be activated using the Shift-F10 key (by default; use the <see cref="Key"/> to change to another key).
+	/// </para>
+	/// <para>
+	/// Callers can cause the ContextMenu to be activated on a right-mouse click (or other interaction) by calling <see cref="Show()"/>.
+	/// </para>
+	/// <para>
+	/// ContextMenus are located using screen using screen coordinates and appear above all other Views.
+	/// </para>
 	/// </summary>
 	/// </summary>
 	public sealed class ContextMenu : IDisposable {
 	public sealed class ContextMenu : IDisposable {
 		private static MenuBar menuBar;
 		private static MenuBar menuBar;
@@ -12,15 +28,15 @@ namespace Terminal.Gui {
 		private Toplevel container;
 		private Toplevel container;
 
 
 		/// <summary>
 		/// <summary>
-		/// Initialize a context menu with empty menu items.
+		/// Initializes a context menu with no menu items.
 		/// </summary>
 		/// </summary>
 		public ContextMenu () : this (0, 0, new MenuBarItem ()) { }
 		public ContextMenu () : this (0, 0, new MenuBarItem ()) { }
 
 
 		/// <summary>
 		/// <summary>
-		/// Initialize a context menu with menu items from a host <see cref="View"/>.
+		/// Initializes a context menu, with a <see cref="View"/> specifiying the parent/hose of the menu.
 		/// </summary>
 		/// </summary>
 		/// <param name="host">The host view.</param>
 		/// <param name="host">The host view.</param>
-		/// <param name="menuItems">The menu items.</param>
+		/// <param name="menuItems">The menu items for the context menu.</param>
 		public ContextMenu (View host, MenuBarItem menuItems) :
 		public ContextMenu (View host, MenuBarItem menuItems) :
 			this (host.Frame.X, host.Frame.Y, menuItems)
 			this (host.Frame.X, host.Frame.Y, menuItems)
 		{
 		{
@@ -28,10 +44,10 @@ namespace Terminal.Gui {
 		}
 		}
 
 
 		/// <summary>
 		/// <summary>
-		/// Initialize a context menu with menu items.
+		/// Initializes a context menu with menu items at a specific screen location.
 		/// </summary>
 		/// </summary>
-		/// <param name="x">The left position.</param>
-		/// <param name="y">The top position.</param>
+		/// <param name="x">The left position (screen relative).</param>
+		/// <param name="y">The top position (screen relative).</param>
 		/// <param name="menuItems">The menu items.</param>
 		/// <param name="menuItems">The menu items.</param>
 		public ContextMenu (int x, int y, MenuBarItem menuItems)
 		public ContextMenu (int x, int y, MenuBarItem menuItems)
 		{
 		{
@@ -48,7 +64,7 @@ namespace Terminal.Gui {
 		}
 		}
 
 
 		/// <summary>
 		/// <summary>
-		/// Disposes the all the context menu objects instances.
+		/// Disposes the context menu object.
 		/// </summary>
 		/// </summary>
 		public void Dispose ()
 		public void Dispose ()
 		{
 		{
@@ -65,7 +81,7 @@ namespace Terminal.Gui {
 		}
 		}
 
 
 		/// <summary>
 		/// <summary>
-		/// Open the <see cref="MenuItems"/> menu items.
+		/// Shows (opens) the ContextMenu, displaying the <see cref="MenuItem"/>s it contains.
 		/// </summary>
 		/// </summary>
 		public void Show ()
 		public void Show ()
 		{
 		{
@@ -110,7 +126,7 @@ namespace Terminal.Gui {
 			} else if (ForceMinimumPosToZero && position.Y < 0) {
 			} else if (ForceMinimumPosToZero && position.Y < 0) {
 				position.Y = 0;
 				position.Y = 0;
 			}
 			}
-
+			
 			menuBar = new MenuBar (new [] { MenuItems }) {
 			menuBar = new MenuBar (new [] { MenuItems }) {
 				X = position.X,
 				X = position.X,
 				Y = position.Y,
 				Y = position.Y,
@@ -139,7 +155,7 @@ namespace Terminal.Gui {
 		}
 		}
 
 
 		/// <summary>
 		/// <summary>
-		/// Close the <see cref="MenuItems"/> menu items.
+		/// Hides (closes) the ContextMenu.
 		/// </summary>
 		/// </summary>
 		public void Hide ()
 		public void Hide ()
 		{
 		{
@@ -158,7 +174,7 @@ namespace Terminal.Gui {
 		public event Action<MouseFlags> MouseFlagsChanged;
 		public event Action<MouseFlags> MouseFlagsChanged;
 
 
 		/// <summary>
 		/// <summary>
-		/// Gets or set the menu position.
+		/// Gets or sets the menu position.
 		/// </summary>
 		/// </summary>
 		public Point Position { get; set; }
 		public Point Position { get; set; }
 
 
@@ -168,7 +184,7 @@ namespace Terminal.Gui {
 		public MenuBarItem MenuItems { get; set; }
 		public MenuBarItem MenuItems { get; set; }
 
 
 		/// <summary>
 		/// <summary>
-		/// The <see cref="Gui.Key"/> used to activate the context menu by keyboard.
+		/// <see cref="Gui.Key"/> specifies they keyboard key that will activate the context menu with the keyboard.
 		/// </summary>
 		/// </summary>
 		public Key Key {
 		public Key Key {
 			get => key;
 			get => key;
@@ -180,7 +196,7 @@ namespace Terminal.Gui {
 		}
 		}
 
 
 		/// <summary>
 		/// <summary>
-		/// The <see cref="Gui.MouseFlags"/> used to activate the context menu by mouse.
+		/// <see cref="Gui.MouseFlags"/> specifies the mouse action used to activate the context menu by mouse.
 		/// </summary>
 		/// </summary>
 		public MouseFlags MouseFlags {
 		public MouseFlags MouseFlags {
 			get => mouseFlags;
 			get => mouseFlags;
@@ -192,7 +208,7 @@ namespace Terminal.Gui {
 		}
 		}
 
 
 		/// <summary>
 		/// <summary>
-		/// Gets information whether menu is showing or not.
+		/// Gets whether the ContextMenu is showing or not.
 		/// </summary>
 		/// </summary>
 		public static bool IsShow { get; private set; }
 		public static bool IsShow { get; private set; }
 
 
@@ -203,8 +219,9 @@ namespace Terminal.Gui {
 		public View Host { get; set; }
 		public View Host { get; set; }
 
 
 		/// <summary>
 		/// <summary>
-		/// Gets or sets whether forces the minimum position to zero
-		/// if the left or right position are negative.
+		/// Sets or gets whether the context menu be forced to the right, ensuring it is not clipped, if the x position 
+		/// is less than zero. The default is <see langword="true"/> which means the context menu will be forced to the right.
+		/// If set to <see langword="false"/>, the context menu will be clipped on the left if x is less than zero.
 		/// </summary>
 		/// </summary>
 		public bool ForceMinimumPosToZero { get; set; } = true;
 		public bool ForceMinimumPosToZero { get; set; } = true;
 
 
@@ -214,7 +231,9 @@ namespace Terminal.Gui {
 		public MenuBar MenuBar { get => menuBar; }
 		public MenuBar MenuBar { get => menuBar; }
 
 
 		/// <summary>
 		/// <summary>
-		/// Gets or sets if the sub-menus must be displayed in a single or multiple frames.
+		/// Gets or sets if sub-menus will be displayed using a "single frame" menu style. If <see langword="true"/>, the ContextMenu
+		/// and any sub-menus that would normally cascade will be displayed within a single frame. If <see langword="false"/> (the default),
+		/// sub-menus will cascade using separate frames for each level of the menu hierarchy.
 		/// </summary>
 		/// </summary>
 		public bool UseSubMenusSingleFrame { get; set; }
 		public bool UseSubMenusSingleFrame { get; set; }
 	}
 	}

+ 119 - 150
Terminal.Gui/Views/Menu.cs

@@ -1,13 +1,3 @@
-//
-// Menu.cs: application menus and submenus
-//
-// Authors:
-//   Miguel de Icaza ([email protected])
-//
-// TODO:
-//   Add accelerator support, but should also support chords (Shortcut in MenuItem)
-//   Allow menus inside menus
-
 using System;
 using System;
 using NStack;
 using NStack;
 using System.Linq;
 using System.Linq;
@@ -21,23 +11,24 @@ namespace Terminal.Gui {
 	[Flags]
 	[Flags]
 	public enum MenuItemCheckStyle {
 	public enum MenuItemCheckStyle {
 		/// <summary>
 		/// <summary>
-		/// The menu item will be shown normally, with no check indicator.
+		/// The menu item will be shown normally, with no check indicator. The default.
 		/// </summary>
 		/// </summary>
 		NoCheck = 0b_0000_0000,
 		NoCheck = 0b_0000_0000,
 
 
 		/// <summary>
 		/// <summary>
-		/// The menu item will indicate checked/un-checked state (see <see cref="Checked"/>.
+		/// The menu item will indicate checked/un-checked state (see <see cref="Checked"/>).
 		/// </summary>
 		/// </summary>
 		Checked = 0b_0000_0001,
 		Checked = 0b_0000_0001,
 
 
 		/// <summary>
 		/// <summary>
-		/// The menu item is part of a menu radio group (see <see cref="Checked"/> and will indicate selected state.
+		/// The menu item is part of a menu radio group (see <see cref="Checked"/>) and will indicate selected state.
 		/// </summary>
 		/// </summary>
 		Radio = 0b_0000_0010,
 		Radio = 0b_0000_0010,
 	};
 	};
 
 
 	/// <summary>
 	/// <summary>
-	/// A <see cref="MenuItem"/> has a title, an associated help text, and an action to execute on activation.
+	/// A <see cref="MenuItem"/> has title, an associated help text, and an action to execute on activation. 
+	/// MenuItems can also have a checked indicator (see <see cref="Checked"/>).
 	/// </summary>
 	/// </summary>
 	public class MenuItem {
 	public class MenuItem {
 		ustring title;
 		ustring title;
@@ -78,14 +69,28 @@ namespace Terminal.Gui {
 		}
 		}
 
 
 		/// <summary>
 		/// <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
+		/// The HotKey is used to activate a <see cref="MenuItem"/> with the keyboard. HotKeys are defined by prefixing the <see cref="Title"/>
+		/// of a MenuItem with an underscore ('_'). 
+		/// <para>
+		/// Pressing Alt-Hotkey for a <see cref="MenuBarItem"/> (menu items on the menu bar) works even if the menu is not active). 
+		/// Once a menu has focus and is active, pressing just the HotKey will activate the MenuItem.
+		/// </para>
+		/// <para>
+		/// For example for a MenuBar with a "_File" MenuBarItem that contains a "_New" MenuItem, Alt-F will open the File menu.
+		/// Pressing the N key will then activate the New MenuItem.
+		/// </para>
+		/// <para>
+		/// See also <see cref="Shortcut"/> which enable global key-bindings to menu items.
+		/// </para>
 		/// </summary>
 		/// </summary>
 		public Rune HotKey;
 		public Rune HotKey;
 
 
 		/// <summary>
 		/// <summary>
-		/// This is the global setting that can be used as a global <see cref="ShortcutHelper.Shortcut"/> to invoke the action on the menu.
+		/// Shortcut defines a key binding to the MenuItem that will invoke the MenuItem's action globally for the <see cref="View"/> that is
+		/// the parent of the <see cref="MenuBar"/> or <see cref="ContextMenu"/> this <see cref="MenuItem"/>.
+		/// <para>
+		/// The <see cref="Key"/> will be drawn on the MenuItem to the right of the <see cref="Title"/> and <see cref="Help"/> text. See <see cref="ShortcutTag"/>.
+		/// </para>
 		/// </summary>
 		/// </summary>
 		public Key Shortcut {
 		public Key Shortcut {
 			get => shortcutHelper.Shortcut;
 			get => shortcutHelper.Shortcut;
@@ -97,12 +102,12 @@ namespace Terminal.Gui {
 		}
 		}
 
 
 		/// <summary>
 		/// <summary>
-		/// The keystroke combination used in the <see cref="ShortcutHelper.ShortcutTag"/> as string.
+		/// Gets the text describing the keystroke combination defined by <see cref="Shortcut"/>.
 		/// </summary>
 		/// </summary>
 		public ustring ShortcutTag => ShortcutHelper.GetShortcutTag (shortcutHelper.Shortcut);
 		public ustring ShortcutTag => ShortcutHelper.GetShortcutTag (shortcutHelper.Shortcut);
 
 
 		/// <summary>
 		/// <summary>
-		/// Gets or sets the title.
+		/// Gets or sets the title of the menu item .
 		/// </summary>
 		/// </summary>
 		/// <value>The title.</value>
 		/// <value>The title.</value>
 		public ustring Title {
 		public ustring Title {
@@ -116,34 +121,46 @@ namespace Terminal.Gui {
 		}
 		}
 
 
 		/// <summary>
 		/// <summary>
-		/// Gets or sets the help text for the menu item.
+		/// Gets or sets the help text for the menu item. The help text is drawn to the right of the <see cref="Title"/>.
 		/// </summary>
 		/// </summary>
 		/// <value>The help text.</value>
 		/// <value>The help text.</value>
 		public ustring Help { get; set; }
 		public ustring Help { get; set; }
 
 
 		/// <summary>
 		/// <summary>
-		/// Gets or sets the action to be invoked when the menu is triggered
+		/// Gets or sets the action to be invoked when the menu item is triggered.
 		/// </summary>
 		/// </summary>
 		/// <value>Method to invoke.</value>
 		/// <value>Method to invoke.</value>
 		public Action Action { get; set; }
 		public Action Action { get; set; }
 
 
 		/// <summary>
 		/// <summary>
-		/// Gets or sets the action to be invoked if the menu can be triggered
+		/// Gets or sets the action to be invoked to determine if the menu can be triggered. If <see cref="CanExecute"/> returns <see langword="true"/>
+		/// the menu item will be enabled. Otherwise, it will be disabled. 
 		/// </summary>
 		/// </summary>
-		/// <value>Function to determine if action is ready to be executed.</value>
+		/// <value>Function to determine if the action is can be executed or not.</value>
 		public Func<bool> CanExecute { get; set; }
 		public Func<bool> CanExecute { get; set; }
 
 
 		/// <summary>
 		/// <summary>
-		/// Shortcut to check if the menu item is enabled
+		/// Returns <see langword="true"/> if the menu item is enabled. This method is a wrapper around <see cref="CanExecute"/>.
 		/// </summary>
 		/// </summary>
 		public bool IsEnabled ()
 		public bool IsEnabled ()
 		{
 		{
 			return CanExecute == null ? true : CanExecute ();
 			return CanExecute == null ? true : CanExecute ();
 		}
 		}
 
 
-		internal int Width => 1 + TitleLength + (Help.ConsoleWidth > 0 ? Help.ConsoleWidth + 2 : 0) +
-			(Checked || CheckType.HasFlag (MenuItemCheckStyle.Checked) || CheckType.HasFlag (MenuItemCheckStyle.Radio) ? 2 : 0) +
-			(ShortcutTag.ConsoleWidth > 0 ? ShortcutTag.ConsoleWidth + 2 : 0) + 2;
+		// 
+		// ┌─────────────────────────────┐
+		// │ Quit  Quit UI Catalog  Ctrl+Q │
+		// └─────────────────────────────┘
+		// ┌─────────────────┐
+		// │ ◌ TopLevel Alt+T │
+		// └─────────────────┘
+		// TODO: Replace the `2` literals with named constants 
+		internal int Width => 1 + // space before Title
+			TitleLength +
+			2 + // space after Title - BUGBUG: This should be 1 
+			(Checked || CheckType.HasFlag (MenuItemCheckStyle.Checked) || CheckType.HasFlag (MenuItemCheckStyle.Radio) ? 2 : 0) + // check glyph + space 
+			(Help.ConsoleWidth > 0 ? 2 + Help.ConsoleWidth : 0) + // Two spaces before Help
+			(ShortcutTag.ConsoleWidth > 0 ? 2 + ShortcutTag.ConsoleWidth : 0); // Pad two spaces before shortcut tag (which are also aligned right)
 
 
 		/// <summary>
 		/// <summary>
 		/// Sets or gets whether the <see cref="MenuItem"/> shows a check indicator or not. See <see cref="MenuItemCheckStyle"/>.
 		/// Sets or gets whether the <see cref="MenuItem"/> shows a check indicator or not. See <see cref="MenuItemCheckStyle"/>.
@@ -151,12 +168,12 @@ namespace Terminal.Gui {
 		public bool Checked { set; get; }
 		public bool Checked { set; get; }
 
 
 		/// <summary>
 		/// <summary>
-		/// Sets or gets the type selection indicator the menu item will be displayed with.
+		/// Sets or gets the <see cref="MenuItemCheckStyle"/> of a menu item where <see cref="Checked"/> is set to <see langword="true"/>.
 		/// </summary>
 		/// </summary>
 		public MenuItemCheckStyle CheckType { get; set; }
 		public MenuItemCheckStyle CheckType { get; set; }
 
 
 		/// <summary>
 		/// <summary>
-		/// Gets or sets the parent for this <see cref="MenuItem"/>.
+		/// Gets the parent for this <see cref="MenuItem"/>.
 		/// </summary>
 		/// </summary>
 		/// <value>The parent.</value>
 		/// <value>The parent.</value>
 		public MenuItem Parent { get; internal set; }
 		public MenuItem Parent { get; internal set; }
@@ -167,7 +184,7 @@ namespace Terminal.Gui {
 		internal bool IsFromSubMenu { get { return Parent != null; } }
 		internal bool IsFromSubMenu { get { return Parent != null; } }
 
 
 		/// <summary>
 		/// <summary>
-		/// Merely a debugging aid to see the interaction with main
+		/// Merely a debugging aid to see the interaction with main.
 		/// </summary>
 		/// </summary>
 		public MenuItem GetMenuItem ()
 		public MenuItem GetMenuItem ()
 		{
 		{
@@ -175,7 +192,7 @@ namespace Terminal.Gui {
 		}
 		}
 
 
 		/// <summary>
 		/// <summary>
-		/// Merely a debugging aid to see the interaction with main
+		/// Merely a debugging aid to see the interaction with main.
 		/// </summary>
 		/// </summary>
 		public bool GetMenuBarItem ()
 		public bool GetMenuBarItem ()
 		{
 		{
@@ -213,14 +230,15 @@ namespace Terminal.Gui {
 	}
 	}
 
 
 	/// <summary>
 	/// <summary>
-	/// A <see cref="MenuBarItem"/> contains <see cref="MenuBarItem"/>s or <see cref="MenuItem"/>s.
+	/// <see cref="MenuBarItem"/> is a menu item on an app's <see cref="MenuBar"/>. 
+	/// MenuBarItems do not support <see cref="MenuItem.Shortcut"/>.
 	/// </summary>
 	/// </summary>
 	public class MenuBarItem : MenuItem {
 	public class MenuBarItem : MenuItem {
 		/// <summary>
 		/// <summary>
 		/// Initializes a new <see cref="MenuBarItem"/> as a <see cref="MenuItem"/>.
 		/// Initializes a new <see cref="MenuBarItem"/> as a <see cref="MenuItem"/>.
 		/// </summary>
 		/// </summary>
 		/// <param name="title">Title for the menu item.</param>
 		/// <param name="title">Title for the menu item.</param>
-		/// <param name="help">Help text to display.</param>
+		/// <param name="help">Help text to display. Will be displayed next to the Title surrounded by parentheses.</param>
 		/// <param name="action">Action to invoke when the menu item is activated.</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="canExecute">Function to determine if the action can currently be executed.</param>
 		/// <param name="parent">The parent <see cref="MenuItem"/> of this if exist, otherwise is null.</param>
 		/// <param name="parent">The parent <see cref="MenuItem"/> of this if exist, otherwise is null.</param>
@@ -289,19 +307,6 @@ namespace Terminal.Gui {
 			}
 			}
 		}
 		}
 
 
-		//static int GetMaxTitleLength (MenuItem [] children)
-		//{
-		//	int maxLength = 0;
-		//	foreach (var item in children) {
-		//		int len = GetMenuBarItemLength (item.Title);
-		//		if (len > maxLength)
-		//			maxLength = len;
-		//		item.IsFromSubMenu = true;
-		//	}
-
-		//	return maxLength;
-		//}
-
 		void SetChildrensParent (MenuItem [] childrens)
 		void SetChildrensParent (MenuItem [] childrens)
 		{
 		{
 			foreach (var child in childrens) {
 			foreach (var child in childrens) {
@@ -363,12 +368,6 @@ namespace Terminal.Gui {
 			Title = title;
 			Title = title;
 		}
 		}
 
 
-		///// <summary>
-		///// Gets or sets the title to display.
-		///// </summary>
-		///// <value>The title.</value>
-		//public ustring Title { get; set; }
-
 		/// <summary>
 		/// <summary>
 		/// Gets or sets an array of <see cref="MenuItem"/> objects that are the children of this <see cref="MenuBarItem"/>
 		/// Gets or sets an array of <see cref="MenuItem"/> objects that are the children of this <see cref="MenuBarItem"/>
 		/// </summary>
 		/// </summary>
@@ -391,8 +390,8 @@ namespace Terminal.Gui {
 			}
 			}
 			int minX = x;
 			int minX = x;
 			int minY = y;
 			int minY = y;
-			int maxW = (items.Max (z => z?.Width) ?? 0) + 2;
-			int maxH = items.Length + 2;
+			int maxW = (items.Max (z => z?.Width) ?? 0) + 2; // This 2 is frame border?
+			int maxH = items.Length + 2; // This 2 is frame border?
 			if (parent != null && x + maxW > Driver.Cols) {
 			if (parent != null && x + maxW > Driver.Cols) {
 				minX = Math.Max (parent.Frame.Right - parent.Frame.Width - maxW, 0);
 				minX = Math.Max (parent.Frame.Right - parent.Frame.Width - maxW, 0);
 			}
 			}
@@ -459,6 +458,7 @@ namespace Terminal.Gui {
 			return GetNormalColor ();
 			return GetNormalColor ();
 		}
 		}
 
 
+		// Draws the Menu, within the Frame
 		public override void Redraw (Rect bounds)
 		public override void Redraw (Rect bounds)
 		{
 		{
 			Driver.SetAttribute (GetNormalColor ());
 			Driver.SetAttribute (GetNormalColor ());
@@ -477,13 +477,14 @@ namespace Terminal.Gui {
 					Move (1, i + 1);
 					Move (1, i + 1);
 
 
 				Driver.SetAttribute (DetermineColorSchemeFor (item, i));
 				Driver.SetAttribute (DetermineColorSchemeFor (item, i));
-				for (int p = Bounds.X; p < Frame.Width - 2; p++) {
+				for (int p = Bounds.X; p < Frame.Width - 2; p++) { // This - 2 is for the border
 					if (p < 0)
 					if (p < 0)
 						continue;
 						continue;
 					if (item == null)
 					if (item == null)
 						Driver.AddRune (Driver.HLine);
 						Driver.AddRune (Driver.HLine);
 					else if (i == 0 && p == 0 && host.UseSubMenusSingleFrame && item.Parent.Parent != null)
 					else if (i == 0 && p == 0 && host.UseSubMenusSingleFrame && item.Parent.Parent != null)
 						Driver.AddRune (Driver.LeftArrow);
 						Driver.AddRune (Driver.LeftArrow);
+					// This `- 3` is left border + right border + one row in from right
 					else if (p == Frame.Width - 3 && barItems.SubMenu (barItems.Children [i]) != null)
 					else if (p == Frame.Width - 3 && barItems.SubMenu (barItems.Children [i]) != null)
 						Driver.AddRune (Driver.RightArrow);
 						Driver.AddRune (Driver.RightArrow);
 					else
 					else
@@ -527,6 +528,7 @@ namespace Terminal.Gui {
 							HotKeySpecifier = MenuBar.HotKeySpecifier,
 							HotKeySpecifier = MenuBar.HotKeySpecifier,
 							Text = textToDraw
 							Text = textToDraw
 						};
 						};
+						// The -3 is left/right border + one space (not sure what for)
 						tf.Draw (ViewToScreen (new Rect (2, i + 1, Frame.Width - 3, 1)),
 						tf.Draw (ViewToScreen (new Rect (2, i + 1, Frame.Width - 3, 1)),
 							i == current ? ColorScheme.Focus : GetNormalColor (),
 							i == current ? ColorScheme.Focus : GetNormalColor (),
 							i == current ? ColorScheme.HotFocus : ColorScheme.HotNormal,
 							i == current ? ColorScheme.HotFocus : ColorScheme.HotNormal,
@@ -832,17 +834,27 @@ namespace Terminal.Gui {
 		}
 		}
 	}
 	}
 
 
-
-
 	/// <summary>
 	/// <summary>
-	/// Provides a menu bar with drop-down and cascading menus. 
+	///	<para>
+	/// Provides a menu bar that spans the top of a <see cref="Toplevel"/> View with drop-down and cascading menus. 
+	///	</para>
+	/// <para>
+	/// By default, any sub-sub-menus (sub-menus of the <see cref="MenuItem"/>s added to <see cref="MenuBarItem"/>s) 
+	/// are displayed in a cascading manner, where each sub-sub-menu pops out of the sub-menu frame
+	/// (either to the right or left, depending on where the sub-menu is relative to the edge of the screen). By setting
+	/// <see cref="UseSubMenusSingleFrame"/> to <see langword="true"/>, this behavior can be changed such that all sub-sub-menus are
+	/// drawn within a single frame below the MenuBar.
+	/// </para>
 	/// </summary>
 	/// </summary>
 	/// <remarks>
 	/// <remarks>
 	///	<para>
 	///	<para>
-	///	The <see cref="MenuBar"/> appears on the first row of the terminal.
+	///	The <see cref="MenuBar"/> appears on the first row of the parent <see cref="Toplevel"/> View and uses the full width.
+	///	</para>
+	///	<para>
+	///	The <see cref="MenuBar"/> provides global hotkeys for the application. See <see cref="MenuItem.HotKey"/>.
 	///	</para>
 	///	</para>
 	///	<para>
 	///	<para>
-	///	The <see cref="MenuBar"/> provides global hotkeys for the application.
+	///	See also: <see cref="ContextMenu"/>
 	///	</para>
 	///	</para>
 	/// </remarks>
 	/// </remarks>
 	public class MenuBar : View {
 	public class MenuBar : View {
@@ -850,7 +862,7 @@ namespace Terminal.Gui {
 		internal int selectedSub;
 		internal int selectedSub;
 
 
 		/// <summary>
 		/// <summary>
-		/// Gets or sets the array of <see cref="MenuBarItem"/>s for the menu. Only set this when the <see cref="MenuBar"/> is visible.
+		/// Gets or sets the array of <see cref="MenuBarItem"/>s for the menu. Only set this after the <see cref="MenuBar"/> is visible.
 		/// </summary>
 		/// </summary>
 		/// <value>The menu array.</value>
 		/// <value>The menu array.</value>
 		public MenuBarItem [] Menus { get; set; }
 		public MenuBarItem [] Menus { get; set; }
@@ -873,7 +885,7 @@ namespace Terminal.Gui {
 
 
 		static ustring shortcutDelimiter = "+";
 		static ustring shortcutDelimiter = "+";
 		/// <summary>
 		/// <summary>
-		/// Used for change the shortcut delimiter separator.
+		/// Sets or gets the shortcut delimiter separator. The default is "+".
 		/// </summary>
 		/// </summary>
 		public static ustring ShortcutDelimiter {
 		public static ustring ShortcutDelimiter {
 			get => shortcutDelimiter;
 			get => shortcutDelimiter;
@@ -893,6 +905,13 @@ namespace Terminal.Gui {
 
 
 		/// <summary>
 		/// <summary>
 		/// Gets or sets if the sub-menus must be displayed in a single or multiple frames.
 		/// Gets or sets if the sub-menus must be displayed in a single or multiple frames.
+		/// <para>
+		/// By default any sub-sub-menus (sub-menus of the main <see cref="MenuItem"/>s) are displayed in a cascading manner, 
+		/// where each sub-sub-menu pops out of the sub-menu frame
+		/// (either to the right or left, depending on where the sub-menu is relative to the edge of the screen). By setting
+		/// <see cref="UseSubMenusSingleFrame"/> to <see langword="true"/>, this behavior can be changed such that all sub-sub-menus are
+		/// drawn within a single frame below the MenuBar.
+		/// </para>		
 		/// </summary>
 		/// </summary>
 		public bool UseSubMenusSingleFrame {
 		public bool UseSubMenusSingleFrame {
 			get => useSubMenusSingleFrame;
 			get => useSubMenusSingleFrame;
@@ -1029,6 +1048,14 @@ namespace Terminal.Gui {
 			isCleaning = false;
 			isCleaning = false;
 		}
 		}
 
 
+		// The column where the MenuBar starts
+		static int xOrigin = 0;
+		// Spaces before the Title
+		static int leftPadding = 1;
+		// Spaces after the Title
+		static int rightPadding = 1;
+		// Spaces after the submenu Title, before Help
+		static int parensAroundHelp = 3;
 		///<inheritdoc/>
 		///<inheritdoc/>
 		public override void Redraw (Rect bounds)
 		public override void Redraw (Rect bounds)
 		{
 		{
@@ -1038,7 +1065,7 @@ namespace Terminal.Gui {
 				Driver.AddRune (' ');
 				Driver.AddRune (' ');
 
 
 			Move (1, 0);
 			Move (1, 0);
-			int pos = 1;
+			int pos = 0;
 
 
 			for (int i = 0; i < Menus.Length; i++) {
 			for (int i = 0; i < Menus.Length; i++) {
 				var menu = Menus [i];
 				var menu = Menus [i];
@@ -1051,8 +1078,9 @@ namespace Terminal.Gui {
 					hotColor = ColorScheme.HotNormal;
 					hotColor = ColorScheme.HotNormal;
 					normalColor = GetNormalColor ();
 					normalColor = GetNormalColor ();
 				}
 				}
-				DrawHotString (menu.Help.IsEmpty ? $" {menu.Title}  " : $" {menu.Title}  {menu.Help}  ", hotColor, normalColor);
-				pos += 1 + menu.TitleLength + (menu.Help.ConsoleWidth > 0 ? menu.Help.ConsoleWidth + 2 : 0) + 2;
+				// Note Help on MenuBar is drawn with parens around it
+				DrawHotString (menu.Help.IsEmpty ? $" {menu.Title} " : $" {menu.Title} ({menu.Help}) ", hotColor, normalColor);
+				pos += leftPadding + menu.TitleLength + (menu.Help.ConsoleWidth > 0 ? leftPadding + menu.Help.ConsoleWidth + parensAroundHelp : 0) + rightPadding;
 			}
 			}
 			PositionCursor ();
 			PositionCursor ();
 		}
 		}
@@ -1067,14 +1095,10 @@ namespace Terminal.Gui {
 			for (int i = 0; i < Menus.Length; i++) {
 			for (int i = 0; i < Menus.Length; i++) {
 				if (i == selected) {
 				if (i == selected) {
 					pos++;
 					pos++;
-					if (IsMenuOpen)
-						Move (pos + 1, 0);
-					else {
-						Move (pos + 1, 0);
-					}
+					Move (pos + 1, 0);
 					return;
 					return;
 				} else {
 				} else {
-					pos += 1 + Menus [i].TitleLength + (Menus [i].Help.ConsoleWidth > 0 ? Menus [i].Help.ConsoleWidth + 2 : 0) + 2;
+					pos += leftPadding + Menus [i].TitleLength + (Menus [i].Help.ConsoleWidth > 0 ? Menus [i].Help.ConsoleWidth + parensAroundHelp : 0) + rightPadding;
 				}
 				}
 			}
 			}
 		}
 		}
@@ -1112,7 +1136,7 @@ namespace Terminal.Gui {
 		public event Action<MenuClosingEventArgs> MenuClosing;
 		public event Action<MenuClosingEventArgs> MenuClosing;
 
 
 		/// <summary>
 		/// <summary>
-		/// Raised when all the menu are closed.
+		/// Raised when all the menu is closed.
 		/// </summary>
 		/// </summary>
 		public event Action MenuAllClosed;
 		public event Action MenuAllClosed;
 
 
@@ -1135,7 +1159,7 @@ namespace Terminal.Gui {
 		internal bool isMenuClosing;
 		internal bool isMenuClosing;
 
 
 		/// <summary>
 		/// <summary>
-		/// True if the menu is open; otherwise false.
+		/// <see langword="true"/> if the menu is open; otherwise <see langword="true"/>.
 		/// </summary>
 		/// </summary>
 		public bool IsMenuOpen { get; protected set; }
 		public bool IsMenuOpen { get; protected set; }
 
 
@@ -1168,7 +1192,7 @@ namespace Terminal.Gui {
 		}
 		}
 
 
 		/// <summary>
 		/// <summary>
-		/// Virtual method that will invoke the <see cref="MenuClosing"/>
+		/// Virtual method that will invoke the <see cref="MenuClosing"/>.
 		/// </summary>
 		/// </summary>
 		/// <param name="currentMenu">The current menu to be closed.</param>
 		/// <param name="currentMenu">The current menu to be closed.</param>
 		/// <param name="reopen">Whether the current menu will be reopen.</param>
 		/// <param name="reopen">Whether the current menu will be reopen.</param>
@@ -1181,7 +1205,7 @@ namespace Terminal.Gui {
 		}
 		}
 
 
 		/// <summary>
 		/// <summary>
-		/// Virtual method that will invoke the <see cref="MenuAllClosed"/>
+		/// Virtual method that will invoke the <see cref="MenuAllClosed"/>.
 		/// </summary>
 		/// </summary>
 		public virtual void OnMenuAllClosed ()
 		public virtual void OnMenuAllClosed ()
 		{
 		{
@@ -1191,7 +1215,7 @@ namespace Terminal.Gui {
 		View lastFocused;
 		View lastFocused;
 
 
 		/// <summary>
 		/// <summary>
-		/// Get the lasted focused view before open the menu.
+		/// Gets the view that was last focused before opening the menu.
 		/// </summary>
 		/// </summary>
 		public View LastFocused { get; private set; }
 		public View LastFocused { get; private set; }
 
 
@@ -1209,6 +1233,7 @@ namespace Terminal.Gui {
 			int pos = 0;
 			int pos = 0;
 			switch (subMenu) {
 			switch (subMenu) {
 			case null:
 			case null:
+				// Open a submenu below a MenuBar
 				lastFocused = lastFocused ?? (SuperView == null ? Application.Current.MostFocused : SuperView.MostFocused);
 				lastFocused = lastFocused ?? (SuperView == null ? Application.Current.MostFocused : SuperView.MostFocused);
 				if (openSubMenu != null && !CloseMenu (false, true))
 				if (openSubMenu != null && !CloseMenu (false, true))
 					return;
 					return;
@@ -1221,8 +1246,10 @@ namespace Terminal.Gui {
 					openMenu.Dispose ();
 					openMenu.Dispose ();
 				}
 				}
 
 
+				// This positions the submenu horizontally aligned with the first character of the
+				// menu it belongs to's text
 				for (int i = 0; i < index; i++)
 				for (int i = 0; i < index; i++)
-					pos += 1 + Menus [i].TitleLength + (Menus [i].Help.ConsoleWidth > 0 ? Menus [i].Help.ConsoleWidth + 2 : 0) + 2;
+					pos += Menus [i].TitleLength + (Menus [i].Help.ConsoleWidth > 0 ? Menus [i].Help.ConsoleWidth + 2 : 0) + leftPadding + rightPadding;
 				openMenu = new Menu (this, Frame.X + pos, Frame.Y + 1, Menus [index]);
 				openMenu = new Menu (this, Frame.X + pos, Frame.Y + 1, Menus [index]);
 				openCurrentMenu = openMenu;
 				openCurrentMenu = openMenu;
 				openCurrentMenu.previousSubFocused = openMenu;
 				openCurrentMenu.previousSubFocused = openMenu;
@@ -1235,6 +1262,7 @@ namespace Terminal.Gui {
 				openMenu.SetFocus ();
 				openMenu.SetFocus ();
 				break;
 				break;
 			default:
 			default:
+				// Opens a submenu next to another submenu (openSubMenu)
 				if (openSubMenu == null)
 				if (openSubMenu == null)
 					openSubMenu = new List<Menu> ();
 					openSubMenu = new List<Menu> ();
 				if (sIndex > -1) {
 				if (sIndex > -1) {
@@ -1275,7 +1303,7 @@ namespace Terminal.Gui {
 		}
 		}
 
 
 		/// <summary>
 		/// <summary>
-		/// Opens the current Menu programatically.
+		/// Opens the Menu programatically, as though the F9 key were pressed.
 		/// </summary>
 		/// </summary>
 		public void OpenMenu ()
 		public void OpenMenu ()
 		{
 		{
@@ -1357,7 +1385,7 @@ namespace Terminal.Gui {
 		}
 		}
 
 
 		/// <summary>
 		/// <summary>
-		/// Closes the current Menu programatically, if open and not canceled.
+		/// Closes the Menu programmatically if open and not canceled (as though F9 were pressed).
 		/// </summary>
 		/// </summary>
 		public bool CloseMenu (bool ignoreUseSubMenusSingleFrame = false)
 		public bool CloseMenu (bool ignoreUseSubMenusSingleFrame = false)
 		{
 		{
@@ -1459,26 +1487,6 @@ namespace Terminal.Gui {
 			if (openSubMenu.Count > 0)
 			if (openSubMenu.Count > 0)
 				openCurrentMenu = openSubMenu.Last ();
 				openCurrentMenu = openSubMenu.Last ();
 
 
-			//if (openMenu.Subviews.Count == 0)
-			//	return;
-			//if (index == 0) {
-			//	//SuperView.SetFocus (previousSubFocused);
-			//	FocusPrev ();
-			//	return;
-			//}
-
-			//for (int i = openMenu.Subviews.Count - 1; i > index; i--) {
-			//	isMenuClosing = true;
-			//	if (openMenu.Subviews.Count - 1 > 0)
-			//		SuperView.SetFocus (openMenu.Subviews [i - 1]);
-			//	else
-			//		SuperView.SetFocus (openMenu);
-			//	if (openMenu != null) {
-			//		Remove (openMenu.Subviews [i]);
-			//		openMenu.Remove (openMenu.Subviews [i]);
-			//	}
-			//	RemoveSubMenu (i);
-			//}
 			isMenuClosing = false;
 			isMenuClosing = false;
 		}
 		}
 
 
@@ -1774,10 +1782,10 @@ namespace Terminal.Gui {
 			if (me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked || me.Flags == MouseFlags.Button1TripleClicked || me.Flags == MouseFlags.Button1Clicked ||
 			if (me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked || me.Flags == MouseFlags.Button1TripleClicked || me.Flags == MouseFlags.Button1Clicked ||
 				(me.Flags == MouseFlags.ReportMousePosition && selected > -1) ||
 				(me.Flags == MouseFlags.ReportMousePosition && selected > -1) ||
 				(me.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition) && selected > -1)) {
 				(me.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition) && selected > -1)) {
-				int pos = 1;
+				int pos = xOrigin;
 				int cx = me.X;
 				int cx = me.X;
 				for (int i = 0; i < Menus.Length; i++) {
 				for (int i = 0; i < Menus.Length; i++) {
-					if (cx >= pos && cx < pos + 1 + Menus [i].TitleLength + Menus [i].Help.ConsoleWidth + 2) {
+					if (cx >= pos && cx < pos + leftPadding + Menus [i].TitleLength + Menus [i].Help.ConsoleWidth + rightPadding) {
 						if (me.Flags == MouseFlags.Button1Clicked) {
 						if (me.Flags == MouseFlags.Button1Clicked) {
 							if (Menus [i].IsTopLevel) {
 							if (Menus [i].IsTopLevel) {
 								var menu = new Menu (this, i, 0, Menus [i]);
 								var menu = new Menu (this, i, 0, Menus [i]);
@@ -1806,7 +1814,7 @@ namespace Terminal.Gui {
 						}
 						}
 						return true;
 						return true;
 					}
 					}
-					pos += 1 + Menus [i].TitleLength + 2;
+					pos += leftPadding + Menus [i].TitleLength + rightPadding;
 				}
 				}
 			}
 			}
 			return false;
 			return false;
@@ -1879,47 +1887,6 @@ namespace Terminal.Gui {
 				handled = false;
 				handled = false;
 				return false;
 				return false;
 			}
 			}
-			//if (me.View != this && me.Flags != MouseFlags.Button1Pressed)
-			//	return true;
-			//else if (me.View != this && me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked) {
-			//	Application.UngrabMouse ();
-			//	host.CloseAllMenus ();
-			//	return true;
-			//}
-
-
-			//if (!(me.View is MenuBar) && !(me.View is Menu) && me.Flags != MouseFlags.Button1Pressed))
-			//	return false;
-
-			//if (Application.MouseGrabView != null) {
-			//	if (me.View is MenuBar || me.View is Menu) {
-			//		me.X -= me.OfX;
-			//		me.Y -= me.OfY;
-			//		me.View.MouseEvent (me);
-			//		return true;
-			//	} else if (!(me.View is MenuBar || me.View is Menu) && me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked) {
-			//		Application.UngrabMouse ();
-			//		CloseAllMenus ();
-			//	}
-			//} else if (!isMenuClosed && selected == -1 && me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked) {
-			//	Application.GrabMouse (this);
-			//	return true;
-			//}
-
-			//if (Application.MouseGrabView != null) {
-			//	if (Application.MouseGrabView == me.View && me.View == current) {
-			//		me.X -= me.OfX;
-			//		me.Y -= me.OfY;
-			//	} else if (me.View != current && me.View is MenuBar && me.View is Menu) {
-			//		Application.UngrabMouse ();
-			//		Application.GrabMouse (me.View);
-			//	} else if (me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked) {
-			//		Application.UngrabMouse ();
-			//		CloseMenu ();
-			//	}
-			//} else if ((!isMenuClosed && selected > -1)) {
-			//	Application.GrabMouse (current);
-			//}
 
 
 			handled = true;
 			handled = true;
 
 
@@ -1973,12 +1940,13 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// </summary>
 		public MenuBarItem NewMenuBarItem { get; set; }
 		public MenuBarItem NewMenuBarItem { get; set; }
 		/// <summary>
 		/// <summary>
-		/// Flag that allows you to cancel the opening of the menu.
+		/// Flag that allows the cancellation of the event. If set to <see langword="true"/> in the
+		/// event handler, the event will be canceled. 
 		/// </summary>
 		/// </summary>
 		public bool Cancel { get; set; }
 		public bool Cancel { get; set; }
 
 
 		/// <summary>
 		/// <summary>
-		/// Initializes a new instance of <see cref="MenuOpeningEventArgs"/>
+		/// Initializes a new instance of <see cref="MenuOpeningEventArgs"/>.
 		/// </summary>
 		/// </summary>
 		/// <param name="currentMenu">The current <see cref="MenuBarItem"/> parent.</param>
 		/// <param name="currentMenu">The current <see cref="MenuBarItem"/> parent.</param>
 		public MenuOpeningEventArgs (MenuBarItem currentMenu)
 		public MenuOpeningEventArgs (MenuBarItem currentMenu)
@@ -1997,7 +1965,7 @@ namespace Terminal.Gui {
 		public MenuBarItem CurrentMenu { get; }
 		public MenuBarItem CurrentMenu { get; }
 
 
 		/// <summary>
 		/// <summary>
-		/// Indicates whether the current menu will be reopen.
+		/// Indicates whether the current menu will reopen.
 		/// </summary>
 		/// </summary>
 		public bool Reopen { get; }
 		public bool Reopen { get; }
 
 
@@ -2007,15 +1975,16 @@ namespace Terminal.Gui {
 		public bool IsSubMenu { get; }
 		public bool IsSubMenu { get; }
 
 
 		/// <summary>
 		/// <summary>
-		/// Flag that allows you to cancel the opening of the menu.
+		/// Flag that allows the cancellation of the event. If set to <see langword="true"/> in the
+		/// event handler, the event will be canceled. 
 		/// </summary>
 		/// </summary>
 		public bool Cancel { get; set; }
 		public bool Cancel { get; set; }
 
 
 		/// <summary>
 		/// <summary>
-		/// Initializes a new instance of <see cref="MenuClosingEventArgs"/>
+		/// Initializes a new instance of <see cref="MenuClosingEventArgs"/>.
 		/// </summary>
 		/// </summary>
 		/// <param name="currentMenu">The current <see cref="MenuBarItem"/> parent.</param>
 		/// <param name="currentMenu">The current <see cref="MenuBarItem"/> parent.</param>
-		/// <param name="reopen">Whether the current menu will be reopen.</param>
+		/// <param name="reopen">Whether the current menu will reopen.</param>
 		/// <param name="isSubMenu">Indicates whether it is a sub-menu.</param>
 		/// <param name="isSubMenu">Indicates whether it is a sub-menu.</param>
 		public MenuClosingEventArgs (MenuBarItem currentMenu, bool reopen, bool isSubMenu)
 		public MenuClosingEventArgs (MenuBarItem currentMenu, bool reopen, bool isSubMenu)
 		{
 		{

+ 6 - 4
UICatalog/Scenarios/CsvEditor.cs

@@ -43,12 +43,14 @@ namespace UICatalog.Scenarios {
 				Height = Dim.Fill (1),
 				Height = Dim.Fill (1),
 			};
 			};
 
 
-			var menu = new MenuBar (new MenuBarItem [] {
-				new MenuBarItem ("_File", new MenuItem [] {
+			var fileMenu = new MenuBarItem ("_File", new MenuItem [] {
 					new MenuItem ("_Open CSV", "", () => Open()),
 					new MenuItem ("_Open CSV", "", () => Open()),
 					new MenuItem ("_Save", "", () => Save()),
 					new MenuItem ("_Save", "", () => Save()),
-					new MenuItem ("_Quit", "", () => Quit()),
-				}),
+					new MenuItem ("_Quit", "Quits The App", () => Quit()),
+				});
+			//fileMenu.Help = "Help";
+			var menu = new MenuBar (new MenuBarItem [] {
+				fileMenu,
 				new MenuBarItem ("_Edit", new MenuItem [] {
 				new MenuBarItem ("_Edit", new MenuItem [] {
 					new MenuItem ("_New Column", "", () => AddColumn()),
 					new MenuItem ("_New Column", "", () => AddColumn()),
 					new MenuItem ("_New Row", "", () => AddRow()),
 					new MenuItem ("_New Row", "", () => AddRow()),

+ 1 - 0
UICatalog/Scenarios/Editor.cs

@@ -116,6 +116,7 @@ namespace UICatalog.Scenarios {
 					new MenuBarItem ("_Languages", GetSupportedCultures ())
 					new MenuBarItem ("_Languages", GetSupportedCultures ())
 				})
 				})
 			});
 			});
+
 			Top.Add (menu);
 			Top.Add (menu);
 
 
 			var statusBar = new StatusBar (new StatusItem [] {
 			var statusBar = new StatusBar (new StatusItem [] {

+ 5 - 4
UICatalog/UICatalog.cs

@@ -169,7 +169,7 @@ namespace UICatalog {
 
 
 			_menu = new MenuBar (new MenuBarItem [] {
 			_menu = new MenuBar (new MenuBarItem [] {
 				new MenuBarItem ("_File", new MenuItem [] {
 				new MenuBarItem ("_File", new MenuItem [] {
-					new MenuItem ("_Quit", "", () => Application.RequestStop(), null, null, Key.Q | Key.CtrlMask)
+					new MenuItem ("_Quit", "Quit UI Catalog", () => Application.RequestStop(), null, null, Key.Q | Key.CtrlMask)
 				}),
 				}),
 				new MenuBarItem ("_Color Scheme", CreateColorSchemeMenuItems()),
 				new MenuBarItem ("_Color Scheme", CreateColorSchemeMenuItems()),
 				new MenuBarItem ("Diag_nostics", CreateDiagnosticMenuItems()),
 				new MenuBarItem ("Diag_nostics", CreateDiagnosticMenuItems()),
@@ -178,7 +178,7 @@ namespace UICatalog {
 					new MenuItem ("gui.cs _README", "", () => OpenUrl ("https://github.com/gui-cs/Terminal.Gui"), null, null, Key.F2),
 					new MenuItem ("gui.cs _README", "", () => OpenUrl ("https://github.com/gui-cs/Terminal.Gui"), null, null, Key.F2),
 					new MenuItem ("_About...",
 					new MenuItem ("_About...",
 						"About UI Catalog", () =>  MessageBox.Query ("About UI Catalog", aboutMessage.ToString(), "_Ok"), null, null, Key.CtrlMask | Key.A),
 						"About UI Catalog", () =>  MessageBox.Query ("About UI Catalog", aboutMessage.ToString(), "_Ok"), null, null, Key.CtrlMask | Key.A),
-				})
+				}),
 			});
 			});
 
 
 			_leftPane = new FrameView ("Categories") {
 			_leftPane = new FrameView ("Categories") {
@@ -318,7 +318,7 @@ namespace UICatalog {
 		{
 		{
 			List<MenuItem> menuItems = new List<MenuItem> ();
 			List<MenuItem> menuItems = new List<MenuItem> ();
 			var item = new MenuItem ();
 			var item = new MenuItem ();
-			item.Title = "_Disable/Enable Mouse";
+			item.Title = "_Disable Mouse";
 			item.Shortcut = Key.CtrlMask | Key.AltMask | (Key)item.Title.ToString ().Substring (1, 1) [0];
 			item.Shortcut = Key.CtrlMask | Key.AltMask | (Key)item.Title.ToString ().Substring (1, 1) [0];
 			item.CheckType |= MenuItemCheckStyle.Checked;
 			item.CheckType |= MenuItemCheckStyle.Checked;
 			item.Checked = Application.IsMouseDisabled;
 			item.Checked = Application.IsMouseDisabled;
@@ -334,7 +334,8 @@ namespace UICatalog {
 
 
 			List<MenuItem> menuItems = new List<MenuItem> ();
 			List<MenuItem> menuItems = new List<MenuItem> ();
 			var item = new MenuItem ();
 			var item = new MenuItem ();
-			item.Title = "Keybindings";
+			item.Title = "_Key Bindings";
+			item.Help = "Change which keys do what";
 			item.Action += () => {
 			item.Action += () => {
 				var dlg = new KeyBindingsDialog ();
 				var dlg = new KeyBindingsDialog ();
 				Application.Run (dlg);
 				Application.Run (dlg);

+ 4 - 4
UnitTests/ContextMenuTests.cs

@@ -592,7 +592,7 @@ namespace Terminal.Gui.Core {
 			Assert.Equal (new Point (9, 3), tf.ContextMenu.Position);
 			Assert.Equal (new Point (9, 3), tf.ContextMenu.Position);
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
 			var expected = @"
 			var expected = @"
-  File   Edit                   
+ File  Edit                     
                                 
                                 
                                 
                                 
   Label: TextField              
   Label: TextField              
@@ -612,7 +612,7 @@ namespace Terminal.Gui.Core {
 ";
 ";
 
 
 			var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
 			var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 32, 17), pos);
+			Assert.Equal (new Rect (1, 0, 32, 17), pos);
 		}
 		}
 
 
 		[Fact, AutoInitShutdown]
 		[Fact, AutoInitShutdown]
@@ -656,7 +656,7 @@ namespace Terminal.Gui.Core {
 			Assert.Equal (new Point (10, 5), tf.ContextMenu.Position);
 			Assert.Equal (new Point (10, 5), tf.ContextMenu.Position);
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
 			var expected = @"
 			var expected = @"
-  File   Edit                               
+ File  Edit                                 
 ┌ Window ──────────────────────────────────┐
 ┌ Window ──────────────────────────────────┐
 │                                          │
 │                                          │
 │                                          │
 │                                          │
@@ -676,7 +676,7 @@ namespace Terminal.Gui.Core {
 ";
 ";
 
 
 			var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
 			var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 44, 17), pos);
+			Assert.Equal (new Rect (1, 0, 44, 17), pos);
 		}
 		}
 
 
 		[Fact, AutoInitShutdown]
 		[Fact, AutoInitShutdown]

+ 280 - 231
UnitTests/MenuTests.cs

@@ -1,7 +1,9 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Linq;
 using Xunit;
 using Xunit;
 using Xunit.Abstractions;
 using Xunit.Abstractions;
+using static Terminal.Gui.Views.MenuTests;
 
 
 namespace Terminal.Gui.Views {
 namespace Terminal.Gui.Views {
 	public class MenuTests {
 	public class MenuTests {
@@ -705,16 +707,15 @@ Edit
 
 
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
 			var expected = @"
 			var expected = @"
-  Numbers
+ Numbers
 ";
 ";
 
 
 			var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
 			var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 9, 1), pos);
 
 
 			Assert.True (menu.ProcessHotKey (new KeyEvent (Key.F9, null)));
 			Assert.True (menu.ProcessHotKey (new KeyEvent (Key.F9, null)));
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
 			expected = @"
 			expected = @"
-  Numbers 
+ Numbers  
 ┌────────┐
 ┌────────┐
 │ One    │
 │ One    │
 │ Two   ►│
 │ Two   ►│
@@ -723,12 +724,11 @@ Edit
 ";
 ";
 
 
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 10, 6), pos);
 
 
 			Assert.True (Application.Top.Subviews [1].ProcessKey (new KeyEvent (Key.CursorDown, null)));
 			Assert.True (Application.Top.Subviews [1].ProcessKey (new KeyEvent (Key.CursorDown, null)));
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
 			expected = @"
 			expected = @"
-  Numbers                
+ Numbers                 
 ┌────────┐               
 ┌────────┐               
 │ One    │               
 │ One    │               
 │ Two   ►│┌─────────────┐
 │ Two   ►│┌─────────────┐
@@ -738,12 +738,11 @@ Edit
 ";
 ";
 
 
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 25, 7), pos);
 
 
 			Assert.True (Application.Top.Subviews [2].ProcessKey (new KeyEvent (Key.CursorLeft, null)));
 			Assert.True (Application.Top.Subviews [2].ProcessKey (new KeyEvent (Key.CursorLeft, null)));
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
 			expected = @"
 			expected = @"
-  Numbers 
+ Numbers  
 ┌────────┐
 ┌────────┐
 │ One    │
 │ One    │
 │ Two   ►│
 │ Two   ►│
@@ -752,16 +751,14 @@ Edit
 ";
 ";
 
 
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 10, 6), pos);
 
 
 			Assert.True (Application.Top.Subviews [1].ProcessKey (new KeyEvent (Key.Esc, null)));
 			Assert.True (Application.Top.Subviews [1].ProcessKey (new KeyEvent (Key.Esc, null)));
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
 			expected = @"
 			expected = @"
-  Numbers
+ Numbers
 ";
 ";
 
 
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 9, 1), pos);
 		}
 		}
 
 
 		[Fact, AutoInitShutdown]
 		[Fact, AutoInitShutdown]
@@ -785,11 +782,11 @@ Edit
 
 
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
 			var expected = @"
 			var expected = @"
-  Numbers
+ Numbers
 ";
 ";
 
 
 			var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
 			var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 9, 1), pos);
+			Assert.Equal (new Rect (1, 0, 8, 1), pos);
 
 
 			Assert.True (menu.MouseEvent (new MouseEvent () {
 			Assert.True (menu.MouseEvent (new MouseEvent () {
 				X = 1,
 				X = 1,
@@ -799,7 +796,7 @@ Edit
 			}));
 			}));
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
 			expected = @"
 			expected = @"
-  Numbers 
+ Numbers  
 ┌────────┐
 ┌────────┐
 │ One    │
 │ One    │
 │ Two   ►│
 │ Two   ►│
@@ -808,7 +805,7 @@ Edit
 ";
 ";
 
 
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 10, 6), pos);
+			Assert.Equal (new Rect (1, 0, 10, 6), pos);
 
 
 			Assert.False (menu.MouseEvent (new MouseEvent () {
 			Assert.False (menu.MouseEvent (new MouseEvent () {
 				X = 1,
 				X = 1,
@@ -818,7 +815,7 @@ Edit
 			}));
 			}));
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
 			expected = @"
 			expected = @"
-  Numbers                
+ Numbers                 
 ┌────────┐               
 ┌────────┐               
 │ One    │               
 │ One    │               
 │ Two   ►│┌─────────────┐
 │ Two   ►│┌─────────────┐
@@ -828,7 +825,7 @@ Edit
 ";
 ";
 
 
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 25, 7), pos);
+			Assert.Equal (new Rect (1, 0, 25, 7), pos);
 
 
 			Assert.False (menu.MouseEvent (new MouseEvent () {
 			Assert.False (menu.MouseEvent (new MouseEvent () {
 				X = 1,
 				X = 1,
@@ -838,7 +835,7 @@ Edit
 			}));
 			}));
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
 			expected = @"
 			expected = @"
-  Numbers 
+ Numbers  
 ┌────────┐
 ┌────────┐
 │ One    │
 │ One    │
 │ Two   ►│
 │ Two   ►│
@@ -847,7 +844,7 @@ Edit
 ";
 ";
 
 
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 10, 6), pos);
+			Assert.Equal (new Rect (1, 0, 10, 6), pos);
 
 
 			Assert.False (menu.MouseEvent (new MouseEvent () {
 			Assert.False (menu.MouseEvent (new MouseEvent () {
 				X = 70,
 				X = 70,
@@ -857,11 +854,11 @@ Edit
 			}));
 			}));
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
 			expected = @"
 			expected = @"
-  Numbers
+ Numbers
 ";
 ";
 
 
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 9, 1), pos);
+			Assert.Equal (new Rect (1, 0, 8, 1), pos);
 		}
 		}
 
 
 		[Fact, AutoInitShutdown]
 		[Fact, AutoInitShutdown]
@@ -887,16 +884,16 @@ Edit
 
 
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
 			var expected = @"
 			var expected = @"
-  Numbers
+ Numbers
 ";
 ";
 
 
 			var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
 			var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 9, 1), pos);
+			Assert.Equal (new Rect (1, 0, 8, 1), pos);
 
 
 			Assert.True (menu.ProcessHotKey (new KeyEvent (Key.F9, null)));
 			Assert.True (menu.ProcessHotKey (new KeyEvent (Key.F9, null)));
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
 			expected = @"
 			expected = @"
-  Numbers 
+ Numbers  
 ┌────────┐
 ┌────────┐
 │ One    │
 │ One    │
 │ Two   ►│
 │ Two   ►│
@@ -905,13 +902,13 @@ Edit
 ";
 ";
 
 
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 10, 6), pos);
+			Assert.Equal (new Rect (1, 0, 10, 6), pos);
 
 
 			Assert.True (Application.Top.Subviews [1].ProcessKey (new KeyEvent (Key.CursorDown, null)));
 			Assert.True (Application.Top.Subviews [1].ProcessKey (new KeyEvent (Key.CursorDown, null)));
 			Assert.True (Application.Top.Subviews [1].ProcessKey (new KeyEvent (Key.Enter, null)));
 			Assert.True (Application.Top.Subviews [1].ProcessKey (new KeyEvent (Key.Enter, null)));
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
 			expected = @"
 			expected = @"
-  Numbers      
+ Numbers       
 ┌─────────────┐
 ┌─────────────┐
 │◄    Two     │
 │◄    Two     │
 ├─────────────┤
 ├─────────────┤
@@ -921,12 +918,12 @@ Edit
 ";
 ";
 
 
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 15, 7), pos);
+			Assert.Equal (new Rect (1, 0, 15, 7), pos);
 
 
 			Assert.True (Application.Top.Subviews [2].ProcessKey (new KeyEvent (Key.Enter, null)));
 			Assert.True (Application.Top.Subviews [2].ProcessKey (new KeyEvent (Key.Enter, null)));
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
 			expected = @"
 			expected = @"
-  Numbers 
+ Numbers  
 ┌────────┐
 ┌────────┐
 │ One    │
 │ One    │
 │ Two   ►│
 │ Two   ►│
@@ -935,16 +932,16 @@ Edit
 ";
 ";
 
 
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 10, 6), pos);
+			Assert.Equal (new Rect (1, 0, 10, 6), pos);
 
 
 			Assert.True (Application.Top.Subviews [1].ProcessKey (new KeyEvent (Key.Esc, null)));
 			Assert.True (Application.Top.Subviews [1].ProcessKey (new KeyEvent (Key.Esc, null)));
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
 			expected = @"
 			expected = @"
-  Numbers
+ Numbers
 ";
 ";
 
 
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 9, 1), pos);
+			Assert.Equal (new Rect (1, 0, 8, 1), pos);
 		}
 		}
 
 
 		[Fact, AutoInitShutdown]
 		[Fact, AutoInitShutdown]
@@ -970,11 +967,11 @@ Edit
 
 
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
 			var expected = @"
 			var expected = @"
-  Numbers
+ Numbers
 ";
 ";
 
 
 			var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
 			var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 9, 1), pos);
+			Assert.Equal (new Rect (1, 0, 8, 1), pos);
 
 
 			Assert.True (menu.MouseEvent (new MouseEvent () {
 			Assert.True (menu.MouseEvent (new MouseEvent () {
 				X = 1,
 				X = 1,
@@ -984,7 +981,7 @@ Edit
 			}));
 			}));
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
 			expected = @"
 			expected = @"
-  Numbers 
+ Numbers  
 ┌────────┐
 ┌────────┐
 │ One    │
 │ One    │
 │ Two   ►│
 │ Two   ►│
@@ -993,7 +990,7 @@ Edit
 ";
 ";
 
 
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 10, 6), pos);
+			Assert.Equal (new Rect (1, 0, 10, 6), pos);
 
 
 			Assert.False (menu.MouseEvent (new MouseEvent () {
 			Assert.False (menu.MouseEvent (new MouseEvent () {
 				X = 1,
 				X = 1,
@@ -1003,7 +1000,7 @@ Edit
 			}));
 			}));
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
 			expected = @"
 			expected = @"
-  Numbers      
+ Numbers       
 ┌─────────────┐
 ┌─────────────┐
 │◄    Two     │
 │◄    Two     │
 ├─────────────┤
 ├─────────────┤
@@ -1013,7 +1010,7 @@ Edit
 ";
 ";
 
 
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 15, 7), pos);
+			Assert.Equal (new Rect (1, 0, 15, 7), pos);
 
 
 			Assert.False (menu.MouseEvent (new MouseEvent () {
 			Assert.False (menu.MouseEvent (new MouseEvent () {
 				X = 1,
 				X = 1,
@@ -1023,7 +1020,7 @@ Edit
 			}));
 			}));
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
 			expected = @"
 			expected = @"
-  Numbers 
+ Numbers  
 ┌────────┐
 ┌────────┐
 │ One    │
 │ One    │
 │ Two   ►│
 │ Two   ►│
@@ -1032,7 +1029,7 @@ Edit
 ";
 ";
 
 
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 10, 6), pos);
+			Assert.Equal (new Rect (1, 0, 10, 6), pos);
 
 
 			Assert.False (menu.MouseEvent (new MouseEvent () {
 			Assert.False (menu.MouseEvent (new MouseEvent () {
 				X = 70,
 				X = 70,
@@ -1042,11 +1039,11 @@ Edit
 			}));
 			}));
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
 			expected = @"
 			expected = @"
-  Numbers
+ Numbers
 ";
 ";
 
 
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 9, 1), pos);
+			Assert.Equal (new Rect (1, 0, 8, 1), pos);
 		}
 		}
 
 
 		[Fact, AutoInitShutdown]
 		[Fact, AutoInitShutdown]
@@ -1074,11 +1071,11 @@ Edit
 			Assert.True (menu.IsMenuOpen);
 			Assert.True (menu.IsMenuOpen);
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
 			var expected = @"
 			var expected = @"
-  File   Edit
+ File  Edit
 ";
 ";
 
 
 			var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
 			var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 13, 1), pos);
+			Assert.Equal (new Rect (1, 0, 11, 1), pos);
 
 
 			Assert.True (menu.ProcessKey (new (Key.N, null)));
 			Assert.True (menu.ProcessKey (new (Key.N, null)));
 			Application.MainLoop.MainIteration ();
 			Application.MainLoop.MainIteration ();
@@ -1088,11 +1085,11 @@ Edit
 			Assert.True (menu.IsMenuOpen);
 			Assert.True (menu.IsMenuOpen);
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
 			expected = @"
 			expected = @"
-  File   Edit
+ File  Edit
 ";
 ";
 
 
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 13, 1), pos);
+			Assert.Equal (new Rect (1, 0, 11, 1), pos);
 
 
 			Assert.True (menu.ProcessKey (new (Key.CursorRight, null)));
 			Assert.True (menu.ProcessKey (new (Key.CursorRight, null)));
 			Assert.True (menu.ProcessKey (new (Key.C, null)));
 			Assert.True (menu.ProcessKey (new (Key.C, null)));
@@ -1100,21 +1097,150 @@ Edit
 			Assert.True (copyAction);
 			Assert.True (copyAction);
 		}
 		}
 
 
+		// Defines the expected strings for a Menu. Currently supports 
+		//   - MenuBar with any number of MenuItems 
+		//   - Each top-level MenuItem can have a SINGLE sub-menu
+		//
+		// TODO: Enable multiple sub-menus
+		// TODO: Enable checked sub-menus
+		// TODO: Enable sub-menus with sub-menus (perhaps better to put this in a separate class with focused unit tests?)
+		//
+		// E.g: 
+		//
+		// File  Edit
+		//  New    Copy
+		public class ExpectedMenuBar : MenuBar {
+			FakeDriver d = ((FakeDriver)Application.Driver);
+
+			// Each MenuBar title has a 1 space pad on each side
+			// See `static int leftPadding` and `static int rightPadding` on line 1037 of Menu.cs
+			public string MenuBarText {
+				get {
+					string txt = string.Empty;
+					foreach (var m in Menus) {
+
+						txt += " " + m.Title.ToString () + " ";
+					}
+					return txt;
+				}
+			}
+
+			// The expected strings when the menu is closed
+			public string ClosedMenuText => MenuBarText + "\n";
+
+			// Padding for the X of the sub menu Frane
+			// Menu.cs - Line 1239 in `internal void OpenMenu` is where the Menu is created
+			string padding (int i)
+			{
+				int n = 0;
+				while (i > 0){
+					n += Menus [i-1].TitleLength + 2;
+					i--;
+				}
+				return new string (' ', n);
+			}
+
+			// Define expected menu frame
+			// "┌──────┐"
+			// "│ New  │"
+			// "└──────┘"
+			// 
+			// The width of the Frame is determined in Menu.cs line 144, where `Width` is calculated
+			//   1 space before the Title and 2 spaces after the Title/Check/Help
+			public string expectedTopRow (int i) => $"{d.ULCorner}{new String (d.HLine.ToString () [0], Menus [i].Children [0].TitleLength + 3)}{d.URCorner}  \n";
+			// The 3 spaces at end are a result of Menu.cs line 1062 where `pos` is calculated (` + spacesAfterTitle`)
+			public string expectedMenuItemRow (int i) => $"{d.VLine} {Menus [i].Children [0].Title}  {d.VLine}   \n";
+			public string expectedBottomRow (int i) => $"{d.LLCorner}{new String (d.HLine.ToString () [0], Menus [i].Children [0].TitleLength + 3)}{d.LRCorner}  \n";
+
+			// The fulll expected string for an open sub menu
+			public string expectedSubMenuOpen (int i) => ClosedMenuText + 
+				(Menus [i].Children.Length > 0 ?
+					padding (i) + expectedTopRow (i) +
+					padding (i) + expectedMenuItemRow (i) +
+					padding (i) + expectedBottomRow (i) 
+				: 
+				"");
+
+			public ExpectedMenuBar (MenuBarItem [] menus) : base (menus)
+			{
+			}
+		}
+
+		[Fact, AutoInitShutdown]
+		public void MenuBar_Submenus_Alignment_Correct ()
+		{
+			// Define the expected menu
+			var expectedMenu = new ExpectedMenuBar (new MenuBarItem [] {
+				new MenuBarItem ("File", new MenuItem [] {
+					new MenuItem ("Really Long Sub Menu", "",  null)
+				}),
+				new MenuBarItem ("123", new MenuItem [] {
+					new MenuItem ("Copy", "", null)
+				}),
+				new MenuBarItem ("Format", new MenuItem [] {
+					new MenuItem ("Word Wrap", "", null)
+				}),
+				new MenuBarItem ("Help", new MenuItem [] {
+					new MenuItem ("About", "", null)
+				}),
+				new MenuBarItem ("1", new MenuItem [] {
+					new MenuItem ("2", "", null)
+				}),
+				new MenuBarItem ("3", new MenuItem [] {
+					new MenuItem ("2", "", null)
+				}),
+				new MenuBarItem ("Last one", new MenuItem [] {
+					new MenuItem ("Test", "", null)
+				})
+			});
+
+			MenuBarItem [] items = new MenuBarItem [expectedMenu.Menus.Length];
+			for (var i = 0; i < expectedMenu.Menus.Length; i++) {
+				items [i] = new MenuBarItem (expectedMenu.Menus [i].Title, new MenuItem [] {
+					new MenuItem (expectedMenu.Menus [i].Children [0].Title, "", null)
+				});
+			}
+			var menu = new MenuBar (items);
+
+			Application.Top.Add (menu);
+
+			Application.Top.Redraw (Application.Top.Bounds);
+			GraphViewTests.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output);
+
+			for (var i = 0; i < expectedMenu.Menus.Length; i++) {
+				menu.OpenMenu (i);
+				Assert.True (menu.IsMenuOpen);
+				Application.Top.Redraw (Application.Top.Bounds);
+				GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (i), output);
+			}
+		}
+
 		[Fact, AutoInitShutdown]
 		[Fact, AutoInitShutdown]
 		public void HotKey_MenuBar_ProcessHotKey_Menu_ProcessKey ()
 		public void HotKey_MenuBar_ProcessHotKey_Menu_ProcessKey ()
 		{
 		{
 			var newAction = false;
 			var newAction = false;
 			var copyAction = false;
 			var copyAction = false;
 
 
-			var menu = new MenuBar (new MenuBarItem [] {
-				new MenuBarItem ("_File", new MenuItem [] {
-					new MenuItem ("_New", "", () => newAction = true)
+			// Define the expected menu
+			var expectedMenu = new ExpectedMenuBar (new MenuBarItem [] {
+				new MenuBarItem ("File", new MenuItem [] {
+					new MenuItem ("New", "",  null)
 				}),
 				}),
-				new MenuBarItem ("_Edit", new MenuItem [] {
-					new MenuItem ("_Copy", "", () => copyAction = true)
+				new MenuBarItem ("Edit", new MenuItem [] {
+					new MenuItem ("Copy", "", null)
 				})
 				})
 			});
 			});
 
 
+			// The real menu
+			var menu = new MenuBar (new MenuBarItem [] {
+				new MenuBarItem ("_" + expectedMenu.Menus[0].Title, new MenuItem [] {
+					new MenuItem ("_" + expectedMenu.Menus[0].Children[0].Title, "",  () => newAction = true)
+				}),
+				new MenuBarItem ("_" + expectedMenu.Menus[1].Title, new MenuItem [] {
+					new MenuItem ("_" + expectedMenu.Menus[1].Children[0].Title, "",  () => copyAction = true)
+				}),
+			});
+
 			Application.Top.Add (menu);
 			Application.Top.Add (menu);
 
 
 			Assert.False (newAction);
 			Assert.False (newAction);
@@ -1123,15 +1249,7 @@ Edit
 			Assert.True (menu.ProcessHotKey (new (Key.AltMask | Key.F, new KeyModifiers () { Alt = true })));
 			Assert.True (menu.ProcessHotKey (new (Key.AltMask | Key.F, new KeyModifiers () { Alt = true })));
 			Assert.True (menu.IsMenuOpen);
 			Assert.True (menu.IsMenuOpen);
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
-			var expected = @"
-  File   Edit
-┌──────┐     
-│ New  │     
-└──────┘     
-";
-
-			var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 13, 4), pos);
+			GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (0), output);
 
 
 			Assert.True (Application.Top.Subviews [1].ProcessKey (new (Key.N, null)));
 			Assert.True (Application.Top.Subviews [1].ProcessKey (new (Key.N, null)));
 			Application.MainLoop.MainIteration ();
 			Application.MainLoop.MainIteration ();
@@ -1140,15 +1258,7 @@ Edit
 			Assert.True (menu.ProcessHotKey (new (Key.AltMask | Key.E, new KeyModifiers () { Alt = true })));
 			Assert.True (menu.ProcessHotKey (new (Key.AltMask | Key.E, new KeyModifiers () { Alt = true })));
 			Assert.True (menu.IsMenuOpen);
 			Assert.True (menu.IsMenuOpen);
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
-			expected = @"
-  File   Edit   
-       ┌───────┐
-       │ Copy  │
-       └───────┘
-";
-
-			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 16, 4), pos);
+			GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (1), output);
 
 
 			Assert.True (Application.Top.Subviews [1].ProcessKey (new (Key.C, null)));
 			Assert.True (Application.Top.Subviews [1].ProcessKey (new (Key.C, null)));
 			Application.MainLoop.MainIteration ();
 			Application.MainLoop.MainIteration ();
@@ -1158,127 +1268,114 @@ Edit
 		[Fact, AutoInitShutdown]
 		[Fact, AutoInitShutdown]
 		public void MenuBar_Position_And_Size_With_HotKeys_Is_The_Same_As_Without_HotKeys ()
 		public void MenuBar_Position_And_Size_With_HotKeys_Is_The_Same_As_Without_HotKeys ()
 		{
 		{
-			// With HotKeys
+			// Define the expected menu
+			var expectedMenu = new ExpectedMenuBar (new MenuBarItem [] {
+				new MenuBarItem ("File", new MenuItem [] {
+					new MenuItem ("12", "",  null)
+				}),
+				new MenuBarItem ("Edit", new MenuItem [] {
+					new MenuItem ("Copy", "", null)
+				})
+			});
+
+			// Test without HotKeys first
 			var menu = new MenuBar (new MenuBarItem [] {
 			var menu = new MenuBar (new MenuBarItem [] {
-				new MenuBarItem ("_File", new MenuItem [] {
-					new MenuItem ("_New", "", null)
+				new MenuBarItem (expectedMenu.Menus[0].Title, new MenuItem [] {
+					new MenuItem (expectedMenu.Menus[0].Children[0].Title, "", null)
 				}),
 				}),
-				new MenuBarItem ("_Edit", new MenuItem [] {
-					new MenuItem ("_Copy", "", null)
+				new MenuBarItem (expectedMenu.Menus[1].Title, new MenuItem [] {
+					new MenuItem (expectedMenu.Menus[1].Children[0].Title, "", null)
 				})
 				})
 			});
 			});
 
 
 			Application.Top.Add (menu);
 			Application.Top.Add (menu);
 
 
+			// Open first
 			Assert.True (menu.ProcessHotKey (new (Key.F9, new KeyModifiers ())));
 			Assert.True (menu.ProcessHotKey (new (Key.F9, new KeyModifiers ())));
 			Assert.True (menu.IsMenuOpen);
 			Assert.True (menu.IsMenuOpen);
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
-			var expected = @"
-  File   Edit
-┌──────┐     
-│ New  │     
-└──────┘     
-";
-
-			var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 13, 4), pos);
+			GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (0), output);
 
 
+			// Open second
 			Assert.True (Application.Top.Subviews [1].ProcessKey (new (Key.CursorRight, null)));
 			Assert.True (Application.Top.Subviews [1].ProcessKey (new (Key.CursorRight, null)));
 			Assert.True (menu.IsMenuOpen);
 			Assert.True (menu.IsMenuOpen);
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
-			expected = @"
-  File   Edit   
-       ┌───────┐
-       │ Copy  │
-       └───────┘
-";
-
-			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 16, 4), pos);
+			GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (1), output);
 
 
+			// Close menu
 			Assert.True (menu.ProcessHotKey (new (Key.F9, new KeyModifiers ())));
 			Assert.True (menu.ProcessHotKey (new (Key.F9, new KeyModifiers ())));
 			Assert.False (menu.IsMenuOpen);
 			Assert.False (menu.IsMenuOpen);
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
-			expected = @"
-  File   Edit
-";
+			GraphViewTests.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output);
 
 
-			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 13, 1), pos);
+			Application.Top.Remove (menu);
 
 
-			// Without HotKeys
+			// Now test WITH HotKeys
 			menu = new MenuBar (new MenuBarItem [] {
 			menu = new MenuBar (new MenuBarItem [] {
-				new MenuBarItem ("File", new MenuItem [] {
-					new MenuItem ("New", "", null)
+				new MenuBarItem ("_" + expectedMenu.Menus[0].Title, new MenuItem [] {
+					new MenuItem ("_" + expectedMenu.Menus[0].Children[0].Title, "",  null)
+				}),
+				new MenuBarItem ("_" + expectedMenu.Menus[1].Title, new MenuItem [] {
+					new MenuItem ("_" + expectedMenu.Menus[1].Children[0].Title, "",  null)
 				}),
 				}),
-				new MenuBarItem ("Edit", new MenuItem [] {
-					new MenuItem ("Copy", "", null)
-				})
 			});
 			});
 
 
+			Application.Top.Add (menu);
+
+			// Open first
 			Assert.True (menu.ProcessHotKey (new (Key.F9, new KeyModifiers ())));
 			Assert.True (menu.ProcessHotKey (new (Key.F9, new KeyModifiers ())));
 			Assert.True (menu.IsMenuOpen);
 			Assert.True (menu.IsMenuOpen);
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
-			expected = @"
-  File   Edit
-┌──────┐     
-│ New  │     
-└──────┘     
-";
-
-			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 13, 4), pos);
+			GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (0), output);
 
 
+			// Open second
 			Assert.True (Application.Top.Subviews [1].ProcessKey (new (Key.CursorRight, null)));
 			Assert.True (Application.Top.Subviews [1].ProcessKey (new (Key.CursorRight, null)));
 			Assert.True (menu.IsMenuOpen);
 			Assert.True (menu.IsMenuOpen);
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
-			expected = @"
-  File   Edit   
-       ┌───────┐
-       │ Copy  │
-       └───────┘
-";
+			GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (1), output);
 
 
-			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 16, 4), pos);
+			// Close menu
+			Assert.True (menu.ProcessHotKey (new (Key.F9, new KeyModifiers ())));
+			Assert.False (menu.IsMenuOpen);
+			Application.Top.Redraw (Application.Top.Bounds);
+			GraphViewTests.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output);
 		}
 		}
 
 
 		[Fact, AutoInitShutdown]
 		[Fact, AutoInitShutdown]
 		public void MenuBar_ButtonPressed_Open_The_Menu_ButtonPressed_Again_Close_The_Menu ()
 		public void MenuBar_ButtonPressed_Open_The_Menu_ButtonPressed_Again_Close_The_Menu ()
 		{
 		{
-			var menu = new MenuBar (new MenuBarItem [] {
+			// Define the expected menu
+			var expectedMenu = new ExpectedMenuBar (new MenuBarItem [] {
 				new MenuBarItem ("File", new MenuItem [] {
 				new MenuBarItem ("File", new MenuItem [] {
-					new MenuItem ("New", "", null)
+					new MenuItem ("Open", "",  null)
 				}),
 				}),
 				new MenuBarItem ("Edit", new MenuItem [] {
 				new MenuBarItem ("Edit", new MenuItem [] {
 					new MenuItem ("Copy", "", null)
 					new MenuItem ("Copy", "", null)
 				})
 				})
 			});
 			});
 
 
+			// Test without HotKeys first
+			var menu = new MenuBar (new MenuBarItem [] {
+				new MenuBarItem ("_" + expectedMenu.Menus[0].Title, new MenuItem [] {
+					new MenuItem ("_" + expectedMenu.Menus[0].Children[0].Title, "",  null)
+				}),
+				new MenuBarItem ("_" + expectedMenu.Menus[1].Title, new MenuItem [] {
+					new MenuItem ("_" + expectedMenu.Menus[1].Children[0].Title, "",  null)
+				}),
+			});
+
 			Application.Top.Add (menu);
 			Application.Top.Add (menu);
 
 
 			Assert.True (menu.MouseEvent (new MouseEvent () { X = 1, Y = 0, Flags = MouseFlags.Button1Pressed, View = menu }));
 			Assert.True (menu.MouseEvent (new MouseEvent () { X = 1, Y = 0, Flags = MouseFlags.Button1Pressed, View = menu }));
 			Assert.True (menu.IsMenuOpen);
 			Assert.True (menu.IsMenuOpen);
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
-			var expected = @"
-  File   Edit
-┌──────┐     
-│ New  │     
-└──────┘     
-";
 
 
-			var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 13, 4), pos);
+			GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (0), output);
 
 
 			Assert.True (menu.MouseEvent (new MouseEvent () { X = 1, Y = 0, Flags = MouseFlags.Button1Pressed, View = menu }));
 			Assert.True (menu.MouseEvent (new MouseEvent () { X = 1, Y = 0, Flags = MouseFlags.Button1Pressed, View = menu }));
 			Assert.False (menu.IsMenuOpen);
 			Assert.False (menu.IsMenuOpen);
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
-			expected = @"
-  File   Edit
-";
-
-			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 13, 1), pos);
+			GraphViewTests.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output);
 		}
 		}
 
 
 		[Fact]
 		[Fact]
@@ -1300,110 +1397,98 @@ Edit
 		[Fact, AutoInitShutdown]
 		[Fact, AutoInitShutdown]
 		public void Parent_MenuItem_Stay_Focused_If_Child_MenuItem_Is_Empty_By_Mouse ()
 		public void Parent_MenuItem_Stay_Focused_If_Child_MenuItem_Is_Empty_By_Mouse ()
 		{
 		{
-			var menu = new MenuBar (new MenuBarItem [] {
+			// File  Edit  Format
+			//┌──────┐    ┌───────┐         
+			//│ New  │    │ Wrap  │         
+			//└──────┘    └───────┘         
+
+			// Define the expected menu
+			var expectedMenu = new ExpectedMenuBar (new MenuBarItem [] {
 				new MenuBarItem ("File", new MenuItem [] {
 				new MenuBarItem ("File", new MenuItem [] {
-					new MenuItem ("New", "", null)
-				}),
-				new MenuBarItem ("Edit", new MenuItem [] {
+					new MenuItem ("New", "",  null)
 				}),
 				}),
+				new MenuBarItem ("Edit", new MenuItem [] {}),
 				new MenuBarItem ("Format", new MenuItem [] {
 				new MenuBarItem ("Format", new MenuItem [] {
 					new MenuItem ("Wrap", "", null)
 					new MenuItem ("Wrap", "", null)
 				})
 				})
 			});
 			});
+
+			var menu = new MenuBar (new MenuBarItem [] {
+				new MenuBarItem (expectedMenu.Menus[0].Title, new MenuItem [] {
+					new MenuItem (expectedMenu.Menus[0].Children[0].Title, "", null)
+				}),
+				new MenuBarItem (expectedMenu.Menus[1].Title, new MenuItem [] {}),
+				new MenuBarItem (expectedMenu.Menus[2].Title, new MenuItem [] {
+					new MenuItem (expectedMenu.Menus[2].Children[0].Title, "", null)
+				})
+			});
+
 			var tf = new TextField () { Y = 2, Width = 10 };
 			var tf = new TextField () { Y = 2, Width = 10 };
 			Application.Top.Add (menu, tf);
 			Application.Top.Add (menu, tf);
-
 			Application.Begin (Application.Top);
 			Application.Begin (Application.Top);
+
 			Assert.True (tf.HasFocus);
 			Assert.True (tf.HasFocus);
 			Assert.True (menu.MouseEvent (new MouseEvent () { X = 1, Y = 0, Flags = MouseFlags.Button1Pressed, View = menu }));
 			Assert.True (menu.MouseEvent (new MouseEvent () { X = 1, Y = 0, Flags = MouseFlags.Button1Pressed, View = menu }));
 			Assert.True (menu.IsMenuOpen);
 			Assert.True (menu.IsMenuOpen);
 			Assert.False (tf.HasFocus);
 			Assert.False (tf.HasFocus);
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
-			var expected = @"
-  File   Edit   Format
-┌──────┐              
-│ New  │              
-└──────┘              
-";
-
-			var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 22, 4), pos);
+			GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (0), output);
 
 
 			Assert.True (menu.MouseEvent (new MouseEvent () { X = 8, Y = 0, Flags = MouseFlags.ReportMousePosition, View = menu }));
 			Assert.True (menu.MouseEvent (new MouseEvent () { X = 8, Y = 0, Flags = MouseFlags.ReportMousePosition, View = menu }));
 			Assert.True (menu.IsMenuOpen);
 			Assert.True (menu.IsMenuOpen);
 			Assert.False (tf.HasFocus);
 			Assert.False (tf.HasFocus);
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
-			expected = @"
-  File   Edit   Format
-";
-
-			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 22, 1), pos);
+			GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (1), output);
 
 
 			Assert.True (menu.MouseEvent (new MouseEvent () { X = 15, Y = 0, Flags = MouseFlags.ReportMousePosition, View = menu }));
 			Assert.True (menu.MouseEvent (new MouseEvent () { X = 15, Y = 0, Flags = MouseFlags.ReportMousePosition, View = menu }));
 			Assert.True (menu.IsMenuOpen);
 			Assert.True (menu.IsMenuOpen);
 			Assert.False (tf.HasFocus);
 			Assert.False (tf.HasFocus);
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
-			expected = @"
-  File   Edit   Format 
-              ┌───────┐
-              │ Wrap  │
-              └───────┘
-";
-
-			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 23, 4), pos);
+			GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (2), output);
 
 
 			Assert.True (menu.MouseEvent (new MouseEvent () { X = 8, Y = 0, Flags = MouseFlags.ReportMousePosition, View = menu }));
 			Assert.True (menu.MouseEvent (new MouseEvent () { X = 8, Y = 0, Flags = MouseFlags.ReportMousePosition, View = menu }));
 			Assert.True (menu.IsMenuOpen);
 			Assert.True (menu.IsMenuOpen);
 			Assert.False (tf.HasFocus);
 			Assert.False (tf.HasFocus);
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
-			expected = @"
-  File   Edit   Format
-";
-
-			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 22, 1), pos);
+			GraphViewTests.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output);
 
 
 			Assert.True (menu.MouseEvent (new MouseEvent () { X = 1, Y = 0, Flags = MouseFlags.ReportMousePosition, View = menu }));
 			Assert.True (menu.MouseEvent (new MouseEvent () { X = 1, Y = 0, Flags = MouseFlags.ReportMousePosition, View = menu }));
 			Assert.True (menu.IsMenuOpen);
 			Assert.True (menu.IsMenuOpen);
 			Assert.False (tf.HasFocus);
 			Assert.False (tf.HasFocus);
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
-			expected = @"
-  File   Edit   Format
-┌──────┐              
-│ New  │              
-└──────┘              
-";
+			GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (0), output);
 
 
-			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 22, 4), pos);
 
 
 			Assert.True (menu.MouseEvent (new MouseEvent () { X = 8, Y = 0, Flags = MouseFlags.Button1Pressed, View = menu }));
 			Assert.True (menu.MouseEvent (new MouseEvent () { X = 8, Y = 0, Flags = MouseFlags.Button1Pressed, View = menu }));
 			Assert.False (menu.IsMenuOpen);
 			Assert.False (menu.IsMenuOpen);
 			Assert.True (tf.HasFocus);
 			Assert.True (tf.HasFocus);
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
-			expected = @"
-  File   Edit   Format
-";
-
-			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 22, 1), pos);
+			GraphViewTests.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output);
 		}
 		}
 
 
 		[Fact, AutoInitShutdown]
 		[Fact, AutoInitShutdown]
 		public void Parent_MenuItem_Stay_Focused_If_Child_MenuItem_Is_Empty_By_Keyboard ()
 		public void Parent_MenuItem_Stay_Focused_If_Child_MenuItem_Is_Empty_By_Keyboard ()
 		{
 		{
-			var menu = new MenuBar (new MenuBarItem [] {
+			var expectedMenu = new ExpectedMenuBar (new MenuBarItem [] {
 				new MenuBarItem ("File", new MenuItem [] {
 				new MenuBarItem ("File", new MenuItem [] {
 					new MenuItem ("New", "", null)
 					new MenuItem ("New", "", null)
 				}),
 				}),
-				new MenuBarItem ("Edit", new MenuItem [] {
-				}),
+				new MenuBarItem ("Edit", Array.Empty<MenuItem> ()),
 				new MenuBarItem ("Format", new MenuItem [] {
 				new MenuBarItem ("Format", new MenuItem [] {
 					new MenuItem ("Wrap", "", null)
 					new MenuItem ("Wrap", "", null)
 				})
 				})
 			});
 			});
+
+			MenuBarItem [] items = new MenuBarItem [expectedMenu.Menus.Length];
+			for (var i = 0; i < expectedMenu.Menus.Length; i++) {
+				items [i] = new MenuBarItem (expectedMenu.Menus [i].Title, expectedMenu.Menus [i].Children.Length > 0 
+					? new MenuItem [] {
+						new MenuItem (expectedMenu.Menus [i].Children [0].Title, "", null),
+					} 
+					: Array.Empty<MenuItem> ());
+			}
+			var menu = new MenuBar (items);
+			
 			var tf = new TextField () { Y = 2, Width = 10 };
 			var tf = new TextField () { Y = 2, Width = 10 };
 			Application.Top.Add (menu, tf);
 			Application.Top.Add (menu, tf);
 
 
@@ -1413,76 +1498,40 @@ Edit
 			Assert.True (menu.IsMenuOpen);
 			Assert.True (menu.IsMenuOpen);
 			Assert.False (tf.HasFocus);
 			Assert.False (tf.HasFocus);
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
-			var expected = @"
-  File   Edit   Format
-┌──────┐              
-│ New  │              
-└──────┘              
-";
-
-			var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 22, 4), pos);
+			GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen(0), output);
 
 
+			// Right - Edit has no sub menu; this tests that no sub menu shows
 			Assert.True (menu.openMenu.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ())));
 			Assert.True (menu.openMenu.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ())));
 			Assert.True (menu.IsMenuOpen);
 			Assert.True (menu.IsMenuOpen);
 			Assert.False (tf.HasFocus);
 			Assert.False (tf.HasFocus);
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
-			expected = @"
-  File   Edit   Format
-";
-
-			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 22, 1), pos);
+			GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (1), output);
 
 
+			// Right - Format
 			Assert.True (menu.openMenu.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ())));
 			Assert.True (menu.openMenu.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ())));
 			Assert.True (menu.IsMenuOpen);
 			Assert.True (menu.IsMenuOpen);
 			Assert.False (tf.HasFocus);
 			Assert.False (tf.HasFocus);
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
-			expected = @"
-  File   Edit   Format 
-              ┌───────┐
-              │ Wrap  │
-              └───────┘
-";
-
-			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 23, 4), pos);
+			GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (2), output);
 
 
+			// Left - Edit
 			Assert.True (menu.openMenu.ProcessKey (new KeyEvent (Key.CursorLeft, new KeyModifiers ())));
 			Assert.True (menu.openMenu.ProcessKey (new KeyEvent (Key.CursorLeft, new KeyModifiers ())));
 			Assert.True (menu.IsMenuOpen);
 			Assert.True (menu.IsMenuOpen);
 			Assert.False (tf.HasFocus);
 			Assert.False (tf.HasFocus);
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
-			expected = @"
-  File   Edit   Format
-";
-
-			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 22, 1), pos);
+			GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (1), output);
 
 
 			Assert.True (menu.openMenu.ProcessKey (new KeyEvent (Key.CursorLeft, new KeyModifiers ())));
 			Assert.True (menu.openMenu.ProcessKey (new KeyEvent (Key.CursorLeft, new KeyModifiers ())));
 			Assert.True (menu.IsMenuOpen);
 			Assert.True (menu.IsMenuOpen);
 			Assert.False (tf.HasFocus);
 			Assert.False (tf.HasFocus);
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
-			expected = @"
-  File   Edit   Format
-┌──────┐              
-│ New  │              
-└──────┘              
-";
-
-			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 22, 4), pos);
+			GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (0), output);
 
 
 			Assert.True (menu.ProcessHotKey (new KeyEvent (Key.F9, new KeyModifiers ())));
 			Assert.True (menu.ProcessHotKey (new KeyEvent (Key.F9, new KeyModifiers ())));
 			Assert.False (menu.IsMenuOpen);
 			Assert.False (menu.IsMenuOpen);
 			Assert.True (tf.HasFocus);
 			Assert.True (tf.HasFocus);
 			Application.Top.Redraw (Application.Top.Bounds);
 			Application.Top.Redraw (Application.Top.Bounds);
-			expected = @"
-  File   Edit   Format
-";
-
-			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (2, 0, 22, 1), pos);
+			GraphViewTests.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output);
 		}
 		}
 
 
 		[Fact, AutoInitShutdown]
 		[Fact, AutoInitShutdown]

+ 2 - 2
UnitTests/PosTests.cs

@@ -247,7 +247,7 @@ namespace Terminal.Gui.Core {
 				win.Frame.Right, win.Frame.Bottom));
 				win.Frame.Right, win.Frame.Bottom));
 			Assert.Equal (new Rect (0, 20, 78, 1), label.Frame);
 			Assert.Equal (new Rect (0, 20, 78, 1), label.Frame);
 			var expected = @"
 			var expected = @"
-  Menu                                                                          
+ Menu                                                                           
 ┌──────────────────────────────────────────────────────────────────────────────┐
 ┌──────────────────────────────────────────────────────────────────────────────┐
 │                                                                              │
 │                                                                              │
 │                                                                              │
 │                                                                              │
@@ -310,7 +310,7 @@ namespace Terminal.Gui.Core {
 				win.Frame.Right, win.Frame.Bottom));
 				win.Frame.Right, win.Frame.Bottom));
 			Assert.Equal (new Rect (0, 20, 78, 1), label.Frame);
 			Assert.Equal (new Rect (0, 20, 78, 1), label.Frame);
 			var expected = @"
 			var expected = @"
-  Menu                                                                          
+ Menu                                                                           
 ┌──────────────────────────────────────────────────────────────────────────────┐
 ┌──────────────────────────────────────────────────────────────────────────────┐
 │                                                                              │
 │                                                                              │
 │                                                                              │
 │                                                                              │