浏览代码

Fixes #2539. Add feature to draw menus with or without border. (#2556)

BDisp 2 年之前
父节点
当前提交
62746f8037
共有 4 个文件被更改,包括 502 次插入267 次删除
  1. 34 19
      Terminal.Gui/Views/Menu.cs
  2. 5 4
      Terminal.Gui/Views/Toplevel.cs
  3. 21 2
      UICatalog/UICatalog.cs
  4. 442 242
      UnitTests/Views/MenuTests.cs

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

@@ -436,15 +436,16 @@ namespace Terminal.Gui {
 		internal int current;
 		internal View previousSubFocused;
 
-		internal static Rect MakeFrame (int x, int y, MenuItem [] items, Menu parent = null)
+		internal static Rect MakeFrame (int x, int y, MenuItem [] items, Menu parent = null, LineStyle border = LineStyle.Single)
 		{
 			if (items == null || items.Length == 0) {
 				return new Rect ();
 			}
 			int minX = x;
 			int minY = y;
-			int maxW = (items.Max (z => z?.Width) ?? 0) + 2; // This 2 is frame border?
-			int maxH = items.Length + 2; // This 2 is frame border?
+			var borderOffset = border != LineStyle.None ? 2 : 0; // This 2 is frame border?
+			int maxW = (items.Max (z => z?.Width) ?? 0) + borderOffset;
+			int maxH = items.Length + borderOffset;
 			if (parent != null && x + maxW > Driver.Cols) {
 				minX = Math.Max (parent.Frame.Right - parent.Frame.Width - maxW, 0);
 			}
@@ -454,8 +455,8 @@ namespace Terminal.Gui {
 			return new Rect (minX, minY, maxW, maxH);
 		}
 
-		public Menu (MenuBar host, int x, int y, MenuBarItem barItems, Menu parent = null)
-			: base (MakeFrame (x, y, barItems.Children, parent))
+		public Menu (MenuBar host, int x, int y, MenuBarItem barItems, Menu parent = null, LineStyle border = LineStyle.Single)
+			: base (MakeFrame (x, y, barItems.Children, parent, border))
 		{
 			this.barItems = barItems;
 			this.host = host;
@@ -477,8 +478,7 @@ namespace Terminal.Gui {
 				WantMousePositionReports = host.WantMousePositionReports;
 			}
 
-			Border.Thickness = new Thickness (1);
-			Border.BorderStyle = LineStyle.Single;
+			BorderStyle = host.MenusBorderStyle;
 
 			if (Application.Current != null) {
 				Application.Current.DrawContentComplete += Current_DrawContentComplete;
@@ -554,7 +554,7 @@ namespace Terminal.Gui {
 				var item = barItems.Children [i];
 				Driver.SetAttribute (item == null ? GetNormalColor ()
 					: i == current ? ColorScheme.Focus : GetNormalColor ());
-				if (item == null) {
+				if (item == null && BorderStyle != LineStyle.None) {
 					Move (-1, i);
 					Driver.AddRune (Driver.LeftTee);
 				} else if (Frame.X < Driver.Cols) {
@@ -577,7 +577,7 @@ namespace Terminal.Gui {
 				}
 
 				if (item == null) {
-					if (SuperView?.Frame.Right - Frame.X > Frame.Width) {
+					if (BorderStyle != LineStyle.None && SuperView?.Frame.Right - Frame.X > Frame.Width) {
 						Move (Frame.Width - 2, i);
 						Driver.AddRune (Driver.RightTee);
 					}
@@ -858,11 +858,12 @@ namespace Terminal.Gui {
 			}
 			host.handled = false;
 			bool disabled;
+			var meYOffset = BorderStyle != LineStyle.None ? 1 : 0;
 			if (me.Flags == MouseFlags.Button1Clicked) {
 				disabled = false;
-				if (me.Y < 1)
+				if (me.Y < meYOffset)
 					return true;
-				var meY = me.Y - 1;
+				var meY = me.Y - meYOffset;
 				if (meY >= barItems.Children.Length)
 					return true;
 				var item = barItems.Children [meY];
@@ -877,14 +878,14 @@ namespace Terminal.Gui {
 				me.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition)) {
 
 				disabled = false;
-				if (me.Y < 1 || me.Y - 1 >= barItems.Children.Length) {
+				if (me.Y < meYOffset || me.Y - meYOffset >= barItems.Children.Length) {
 					return true;
 				}
-				var item = barItems.Children [me.Y - 1];
+				var item = barItems.Children [me.Y - meYOffset];
 				if (item == null) return true;
 				if (item == null || !item.IsEnabled ()) disabled = true;
 				if (item != null && !disabled)
-					current = me.Y - 1;
+					current = me.Y - meYOffset;
 				if (host.UseSubMenusSingleFrame || !CheckSubMenu ()) {
 					SetNeedsDisplay ();
 					SetParentSetNeedsDisplay ();
@@ -988,6 +989,11 @@ namespace Terminal.Gui {
 		/// <value>The menu array.</value>
 		public MenuBarItem [] Menus { get; set; }
 
+		/// <summary>
+		/// The default <see cref="LineStyle"/> for <see cref="Menus"/>'s border. The default is <see cref="LineStyle.Single"/>.
+		/// </summary>
+		public LineStyle MenusBorderStyle { get; set; } = LineStyle.Single;
+
 		private bool useKeysUpDownAsKeysLeftRight = false;
 
 		/// <summary>
@@ -1391,7 +1397,7 @@ namespace Terminal.Gui {
 				} else {
 					locationOffset = new Point (superView.Frame.X, superView.Frame.Y);
 				}
-				openMenu = new Menu (this, Frame.X + pos + locationOffset.X, Frame.Y + 1 + locationOffset.Y, Menus [index]);
+				openMenu = new Menu (this, Frame.X + pos + locationOffset.X, Frame.Y + 1 + locationOffset.Y, Menus [index], null, MenusBorderStyle);
 				openCurrentMenu = openMenu;
 				openCurrentMenu.previousSubFocused = openMenu;
 
@@ -1407,7 +1413,8 @@ namespace Terminal.Gui {
 				} else {
 					var last = openSubMenu.Count > 0 ? openSubMenu.Last () : openMenu;
 					if (!UseSubMenusSingleFrame) {
-						openCurrentMenu = new Menu (this, last.Frame.Left + last.Frame.Width, last.Frame.Top + 1 + last.current, subMenu, last);
+						locationOffset = GetLocationOffset ();
+						openCurrentMenu = new Menu (this, last.Frame.Left + last.Frame.Width + locationOffset.X, last.Frame.Top + locationOffset.Y + last.current, subMenu, last, MenusBorderStyle);
 					} else {
 						var first = openSubMenu.Count > 0 ? openSubMenu.First () : openMenu;
 						var mbi = new MenuItem [2 + subMenu.Children.Length];
@@ -1418,7 +1425,7 @@ namespace Terminal.Gui {
 						}
 						var newSubMenu = new MenuBarItem (mbi);
 						ViewToScreen (first.Frame.Left, first.Frame.Top, out int rx, out int ry);
-						openCurrentMenu = new Menu (this, rx, ry, newSubMenu);
+						openCurrentMenu = new Menu (this, rx, ry, newSubMenu, null, MenusBorderStyle);
 						last.Visible = false;
 						Application.GrabMouse (openCurrentMenu);
 					}
@@ -1436,6 +1443,14 @@ namespace Terminal.Gui {
 			IsMenuOpen = true;
 		}
 
+		Point GetLocationOffset ()
+		{
+			if (MenusBorderStyle != LineStyle.None) {
+				return new Point (0, 1);
+			}
+			return new Point (-2, 0);
+		}
+
 		/// <summary>
 		/// Opens the Menu programatically, as though the F9 key were pressed.
 		/// </summary>
@@ -1792,7 +1807,7 @@ namespace Terminal.Gui {
 
 			if (mi.IsTopLevel) {
 				ViewToScreen (i, 0, out int rx, out int ry);
-				var menu = new Menu (this, rx, ry, mi);
+				var menu = new Menu (this, rx, ry, mi, null, MenusBorderStyle);
 				menu.Run (mi.Action);
 				menu.Dispose ();
 			} else {
@@ -1914,7 +1929,7 @@ namespace Terminal.Gui {
 						if (me.Flags == MouseFlags.Button1Clicked) {
 							if (Menus [i].IsTopLevel) {
 								ViewToScreen (i, 0, out int rx, out int ry);
-								var menu = new Menu (this, rx, ry, Menus [i]);
+								var menu = new Menu (this, rx, ry, Menus [i], null, MenusBorderStyle);
 								menu.Run (Menus [i].Action);
 								menu.Dispose ();
 							} else if (!IsMenuOpen) {

+ 5 - 4
Terminal.Gui/Views/Toplevel.cs

@@ -777,10 +777,11 @@ namespace Terminal.Gui {
 				}
 				base.Redraw (Bounds);
 
-				if (this.MenuBar != null && this.MenuBar.IsMenuOpen && this.MenuBar.openMenu != null) {
-					// TODO: Hack until we can get compositing working right.
-					this.MenuBar.openMenu.Redraw (this.MenuBar.openMenu.Bounds);
-				}
+				// This is causing the menus drawn incorrectly if UseSubMenusSingleFrame is true
+				//if (this.MenuBar != null && this.MenuBar.IsMenuOpen && this.MenuBar.openMenu != null) {
+				//	// TODO: Hack until we can get compositing working right.
+				//	this.MenuBar.openMenu.Redraw (this.MenuBar.openMenu.Bounds);
+				//}
 			}
 		}
 

+ 21 - 2
UICatalog/UICatalog.cs

@@ -249,6 +249,7 @@ namespace UICatalog {
 		/// the command line) and each time a Scenario ends.
 		/// </summary>
 		public class UICatalogTopLevel : Toplevel {
+			public MenuItem? miIsMenuBorderDisabled;
 			public MenuItem? miIsMouseDisabled;
 			public MenuItem? miEnableConsoleScrolling;
 
@@ -343,7 +344,7 @@ namespace UICatalog {
 				//ContentPane.Tiles.ElementAt (0).ContentView.Add (CategoryListView);
 
 				ScenarioListView = new ListView () {
-					X = Pos.Right(CategoryListView) - 1,
+					X = Pos.Right (CategoryListView) - 1,
 					Y = 1,
 					Width = Dim.Fill (0),
 					Height = Dim.Fill (1),
@@ -447,11 +448,29 @@ namespace UICatalog {
 					new MenuItem [] { },
 					CreateEnableConsoleScrollingMenuItems (),
 					CreateDisabledEnabledMouseItems (),
+					CreateDisabledEnabledMenuBorder (),
 					CreateKeybindingsMenuItems ()
 				};
 				return menuItems;
 			}
 
+			MenuItem [] CreateDisabledEnabledMenuBorder ()
+			{
+				List<MenuItem> menuItems = new List<MenuItem> ();
+				miIsMenuBorderDisabled = new MenuItem {
+					Title = "Disable _Menu Border"
+				};
+				miIsMenuBorderDisabled.Shortcut = Key.CtrlMask | Key.AltMask | (Key)miIsMenuBorderDisabled!.Title!.ToString ()!.Substring (1, 1) [0];
+				miIsMenuBorderDisabled.CheckType |= MenuItemCheckStyle.Checked;
+				miIsMenuBorderDisabled.Action += () => {
+					miIsMenuBorderDisabled.Checked = (bool)!miIsMenuBorderDisabled.Checked!;
+					MenuBar.MenusBorderStyle = !(bool)miIsMenuBorderDisabled.Checked ? LineStyle.Single : LineStyle.None;
+				};
+				menuItems.Add (miIsMenuBorderDisabled);
+
+				return menuItems.ToArray ();
+			}
+
 			MenuItem [] CreateDisabledEnabledMouseItems ()
 			{
 				List<MenuItem> menuItems = new List<MenuItem> ();
@@ -676,7 +695,7 @@ namespace UICatalog {
 				miEnableConsoleScrolling!.Checked = Application.EnableConsoleScrolling;
 
 				var height = (UICatalogApp.ShowStatusBar ? 1 : 0);// + (MenuBar.Visible ? 1 : 0);
-				//ContentPane.Height = Dim.Fill (height);
+										  //ContentPane.Height = Dim.Fill (height);
 
 				StatusBar.Visible = UICatalogApp.ShowStatusBar;
 

+ 442 - 242
UnitTests/Views/MenuTests.cs

@@ -18,12 +18,14 @@ namespace Terminal.Gui.ViewsTests {
 		[Fact]
 		public void Constuctors_Defaults ()
 		{
-			var menu = new Menu (new MenuBar (), 0, 0, new MenuBarItem ());
+			var menuBar = new MenuBar ();
+			var menu = new Menu (menuBar, 0, 0, new MenuBarItem (), null, menuBar.MenusBorderStyle);
 			Assert.Equal (Colors.Menu, menu.ColorScheme);
 			Assert.True (menu.CanFocus);
 			Assert.False (menu.WantContinuousButtonPressed);
+			Assert.Equal (LineStyle.Single, menuBar.MenusBorderStyle);
 
-			var menuBar = new MenuBar ();
+			menuBar = new MenuBar ();
 			Assert.Equal (0, menuBar.X);
 			Assert.Equal (0, menuBar.Y);
 			Assert.IsType<Dim.DimFill> (menuBar.Width);
@@ -861,246 +863,244 @@ Edit
 			Assert.Equal (new Rect (1, 0, 8, 1), pos);
 		}
 
-		// BUGBUG: Tig broke this in #2483 and is not sure why
-//		[Fact, AutoInitShutdown]
-//		public void UseSubMenusSingleFrame_True_By_Keyboard ()
-//		{
-//			var menu = new MenuBar (new MenuBarItem [] {
-//				new MenuBarItem ("Numbers", new MenuItem [] {
-//					new MenuItem ("One", "", null),
-//					new MenuBarItem ("Two", new MenuItem [] {
-//						new MenuItem ("Sub-Menu 1", "", null),
-//						new MenuItem ("Sub-Menu 2", "", null)
-//					}),
-//					new MenuItem ("Three", "", null),
-//				})
-//			});
-
-//			Application.Top.Add (menu);
-//			Application.Begin (Application.Top);
-
-//			Assert.Equal (Point.Empty, new Point (menu.Frame.X, menu.Frame.Y));
-//			Assert.False (menu.UseSubMenusSingleFrame);
-//			menu.UseSubMenusSingleFrame = true;
-//			Assert.True (menu.UseSubMenusSingleFrame);
-
-//			Application.Top.Redraw (Application.Top.Bounds);
-//			var expected = @"
-// Numbers
-//";
-
-//			var pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
-//			Assert.Equal (new Rect (1, 0, 8, 1), pos);
-
-//			Assert.True (menu.ProcessHotKey (new KeyEvent (Key.F9, null)));
-//			Application.Top.Redraw (Application.Top.Bounds);
-//			expected = @"
-// Numbers  
-//┌────────┐
-//│ One    │
-//│ Two   ►│
-//│ Three  │
-//└────────┘
-//";
-
-//			pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
-//			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.Enter, null)));
-//			Application.Top.Redraw (Application.Top.Bounds);
-//			expected = @"
-// Numbers       
-//┌─────────────┐
-//│◄    Two     │
-//├─────────────┤
-//│ Sub-Menu 1  │
-//│ Sub-Menu 2  │
-//└─────────────┘
-//";
-
-//			pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
-//			Assert.Equal (new Rect (1, 0, 15, 7), pos);
-
-//			Assert.True (Application.Top.Subviews [2].ProcessKey (new KeyEvent (Key.Enter, null)));
-//			Application.Top.Redraw (Application.Top.Bounds);
-//			expected = @"
-// Numbers  
-//┌────────┐
-//│ One    │
-//│ Two   ►│
-//│ Three  │
-//└────────┘
-//";
-
-//			pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
-//			Assert.Equal (new Rect (1, 0, 10, 6), pos);
-
-//			Assert.True (Application.Top.Subviews [1].ProcessKey (new KeyEvent (Key.Esc, null)));
-//			Application.Top.Redraw (Application.Top.Bounds);
-//			expected = @"
-// Numbers
-//";
-
-//			pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
-//			Assert.Equal (new Rect (1, 0, 8, 1), pos);
-//		}
-
-		// BUGBUG: Tig broke this in #2483 and is not sure why
-//		[Fact, AutoInitShutdown]
-//		public void UseSubMenusSingleFrame_True_By_Mouse ()
-//		{
-//			var menu = new MenuBar (new MenuBarItem [] {
-//				new MenuBarItem ("Numbers", new MenuItem [] {
-//					new MenuItem ("One", "", null),
-//					new MenuBarItem ("Two", new MenuItem [] {
-//						new MenuItem ("Sub-Menu 1", "", null),
-//						new MenuItem ("Sub-Menu 2", "", null)
-//					}),
-//					new MenuItem ("Three", "", null),
-//				})
-//			});
-
-//			Application.Top.Add (menu);
-//			Application.Begin (Application.Top);
-
-//			Assert.Equal (Point.Empty, new Point (menu.Frame.X, menu.Frame.Y));
-//			Assert.False (menu.UseSubMenusSingleFrame);
-//			menu.UseSubMenusSingleFrame = true;
-//			Assert.True (menu.UseSubMenusSingleFrame);
-
-//			Application.Top.Redraw (Application.Top.Bounds);
-//			var expected = @"
-// Numbers
-//";
-
-//			var pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
-//			Assert.Equal (new Rect (1, 0, 8, 1), pos);
-
-//			Assert.True (menu.MouseEvent (new MouseEvent () {
-//				X = 1,
-//				Y = 0,
-//				Flags = MouseFlags.Button1Pressed,
-//				View = menu
-//			}));
-//			Application.Top.Redraw (Application.Top.Bounds);
-//			expected = @"
-// Numbers  
-//┌────────┐
-//│ One    │
-//│ Two   ►│
-//│ Three  │
-//└────────┘
-//";
-
-//			pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
-//			Assert.Equal (new Rect (1, 0, 10, 6), pos);
-
-//			Assert.False (menu.MouseEvent (new MouseEvent () {
-//				X = 1,
-//				Y = 3,
-//				Flags = MouseFlags.Button1Clicked,
-//				View = Application.Top.Subviews [1]
-//			}));
-//			Application.Top.Redraw (Application.Top.Bounds);
-//			expected = @"
-// Numbers       
-//┌─────────────┐
-//│◄    Two     │
-//├─────────────┤
-//│ Sub-Menu 1  │
-//│ Sub-Menu 2  │
-//└─────────────┘
-//";
-
-//			pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
-//			Assert.Equal (new Rect (1, 0, 15, 7), pos);
-
-//			Assert.False (menu.MouseEvent (new MouseEvent () {
-//				X = 1,
-//				Y = 2,
-//				Flags = MouseFlags.Button1Clicked,
-//				View = Application.Top.Subviews [2]
-//			}));
-//			Application.Top.Redraw (Application.Top.Bounds);
-//			expected = @"
-// Numbers  
-//┌────────┐
-//│ One    │
-//│ Two   ►│
-//│ Three  │
-//└────────┘
-//";
-
-//			pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
-//			Assert.Equal (new Rect (1, 0, 10, 6), pos);
-
-//			Assert.False (menu.MouseEvent (new MouseEvent () {
-//				X = 70,
-//				Y = 2,
-//				Flags = MouseFlags.Button1Clicked,
-//				View = Application.Top
-//			}));
-//			Application.Top.Redraw (Application.Top.Bounds);
-//			expected = @"
-// Numbers
-//";
-
-//			pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
-//			Assert.Equal (new Rect (1, 0, 8, 1), pos);
-//		}
-
-//		[Fact, AutoInitShutdown]
-//		public void HotKey_MenuBar_OnKeyDown_OnKeyUp_ProcessHotKey_ProcessKey ()
-//		{
-//			var newAction = false;
-//			var copyAction = false;
-
-//			var menu = new MenuBar (new MenuBarItem [] {
-//				new MenuBarItem ("_File", new MenuItem [] {
-//					new MenuItem ("_New", "", () => newAction = true)
-//				}),
-//				new MenuBarItem ("_Edit", new MenuItem [] {
-//					new MenuItem ("_Copy", "", () => copyAction = true)
-//				})
-//			});
-
-//			Application.Top.Add (menu);
-//			Application.Begin (Application.Top);
-
-//			Assert.False (newAction);
-//			Assert.False (copyAction);
-
-//			Assert.False (menu.OnKeyDown (new (Key.AltMask, new KeyModifiers () { Alt = true })));
-//			Assert.True (menu.OnKeyUp (new (Key.AltMask, new KeyModifiers () { Alt = true })));
-//			Assert.True (menu.IsMenuOpen);
-//			Application.Top.Redraw (Application.Top.Bounds);
-//			var expected = @"
-// File  Edit
-//";
-
-//			var pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
-//			Assert.Equal (new Rect (1, 0, 11, 1), pos);
-
-//			Assert.True (menu.ProcessKey (new (Key.N, null)));
-//			Application.MainLoop.RunIteration ();
-//			Assert.True (newAction);
-
-//			Assert.True (menu.ProcessHotKey (new (Key.AltMask, new KeyModifiers () { Alt = true })));
-//			Assert.True (menu.IsMenuOpen);
-//			Application.Top.Redraw (Application.Top.Bounds);
-//			expected = @"
-// File  Edit
-//";
-
-//			pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
-//			Assert.Equal (new Rect (1, 0, 11, 1), pos);
-
-//			Assert.True (menu.ProcessKey (new (Key.CursorRight, null)));
-//			Assert.True (menu.ProcessKey (new (Key.C, null)));
-//			Application.MainLoop.RunIteration ();
-//			Assert.True (copyAction);
-//		}
+		[Fact, AutoInitShutdown]
+		public void UseSubMenusSingleFrame_True_By_Keyboard ()
+		{
+			var menu = new MenuBar (new MenuBarItem [] {
+				new MenuBarItem ("Numbers", new MenuItem [] {
+					new MenuItem ("One", "", null),
+					new MenuBarItem ("Two", new MenuItem [] {
+						new MenuItem ("Sub-Menu 1", "", null),
+						new MenuItem ("Sub-Menu 2", "", null)
+					}),
+					new MenuItem ("Three", "", null),
+				})
+			});
+
+			Application.Top.Add (menu);
+			Application.Begin (Application.Top);
+
+			Assert.Equal (Point.Empty, new Point (menu.Frame.X, menu.Frame.Y));
+			Assert.False (menu.UseSubMenusSingleFrame);
+			menu.UseSubMenusSingleFrame = true;
+			Assert.True (menu.UseSubMenusSingleFrame);
+
+			Application.Top.Redraw (Application.Top.Bounds);
+			var expected = @"
+ Numbers
+";
+
+			var pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (1, 0, 8, 1), pos);
+
+			Assert.True (menu.ProcessHotKey (new KeyEvent (Key.F9, null)));
+			Application.Top.Redraw (Application.Top.Bounds);
+			expected = @"
+ Numbers  
+┌────────┐
+│ One    │
+│ Two   ►│
+│ Three  │
+└────────┘
+";
+
+			pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
+			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.Enter, null)));
+			Application.Top.Redraw (Application.Top.Bounds);
+			expected = @"
+ Numbers       
+┌─────────────┐
+│◄    Two     │
+├─────────────┤
+│ Sub-Menu 1  │
+│ Sub-Menu 2  │
+└─────────────┘
+";
+
+			pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (1, 0, 15, 7), pos);
+
+			Assert.True (Application.Top.Subviews [2].ProcessKey (new KeyEvent (Key.Enter, null)));
+			Application.Top.Redraw (Application.Top.Bounds);
+			expected = @"
+ Numbers  
+┌────────┐
+│ One    │
+│ Two   ►│
+│ Three  │
+└────────┘
+";
+
+			pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (1, 0, 10, 6), pos);
+
+			Assert.True (Application.Top.Subviews [1].ProcessKey (new KeyEvent (Key.Esc, null)));
+			Application.Top.Redraw (Application.Top.Bounds);
+			expected = @"
+ Numbers
+";
+
+			pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (1, 0, 8, 1), pos);
+		}
+
+		[Fact, AutoInitShutdown]
+		public void UseSubMenusSingleFrame_True_By_Mouse ()
+		{
+			var menu = new MenuBar (new MenuBarItem [] {
+				new MenuBarItem ("Numbers", new MenuItem [] {
+					new MenuItem ("One", "", null),
+					new MenuBarItem ("Two", new MenuItem [] {
+						new MenuItem ("Sub-Menu 1", "", null),
+						new MenuItem ("Sub-Menu 2", "", null)
+					}),
+					new MenuItem ("Three", "", null),
+				})
+			});
+
+			Application.Top.Add (menu);
+			Application.Begin (Application.Top);
+
+			Assert.Equal (Point.Empty, new Point (menu.Frame.X, menu.Frame.Y));
+			Assert.False (menu.UseSubMenusSingleFrame);
+			menu.UseSubMenusSingleFrame = true;
+			Assert.True (menu.UseSubMenusSingleFrame);
+
+			Application.Top.Redraw (Application.Top.Bounds);
+			var expected = @"
+ Numbers
+";
+
+			var pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (1, 0, 8, 1), pos);
+
+			Assert.True (menu.MouseEvent (new MouseEvent () {
+				X = 1,
+				Y = 0,
+				Flags = MouseFlags.Button1Pressed,
+				View = menu
+			}));
+			Application.Top.Redraw (Application.Top.Bounds);
+			expected = @"
+ Numbers  
+┌────────┐
+│ One    │
+│ Two   ►│
+│ Three  │
+└────────┘
+";
+
+			pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (1, 0, 10, 6), pos);
+
+			Assert.False (menu.MouseEvent (new MouseEvent () {
+				X = 1,
+				Y = 3,
+				Flags = MouseFlags.Button1Clicked,
+				View = Application.Top.Subviews [1]
+			}));
+			Application.Top.Redraw (Application.Top.Bounds);
+			expected = @"
+ Numbers       
+┌─────────────┐
+│◄    Two     │
+├─────────────┤
+│ Sub-Menu 1  │
+│ Sub-Menu 2  │
+└─────────────┘
+";
+
+			pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (1, 0, 15, 7), pos);
+
+			Assert.False (menu.MouseEvent (new MouseEvent () {
+				X = 1,
+				Y = 2,
+				Flags = MouseFlags.Button1Clicked,
+				View = Application.Top.Subviews [2]
+			}));
+			Application.Top.Redraw (Application.Top.Bounds);
+			expected = @"
+ Numbers  
+┌────────┐
+│ One    │
+│ Two   ►│
+│ Three  │
+└────────┘
+";
+
+			pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (1, 0, 10, 6), pos);
+
+			Assert.False (menu.MouseEvent (new MouseEvent () {
+				X = 70,
+				Y = 2,
+				Flags = MouseFlags.Button1Clicked,
+				View = Application.Top
+			}));
+			Application.Top.Redraw (Application.Top.Bounds);
+			expected = @"
+ Numbers
+";
+
+			pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (1, 0, 8, 1), pos);
+		}
+
+		[Fact, AutoInitShutdown]
+		public void HotKey_MenuBar_OnKeyDown_OnKeyUp_ProcessHotKey_ProcessKey ()
+		{
+			var newAction = false;
+			var copyAction = false;
+
+			var menu = new MenuBar (new MenuBarItem [] {
+				new MenuBarItem ("_File", new MenuItem [] {
+					new MenuItem ("_New", "", () => newAction = true)
+				}),
+				new MenuBarItem ("_Edit", new MenuItem [] {
+					new MenuItem ("_Copy", "", () => copyAction = true)
+				})
+			});
+
+			Application.Top.Add (menu);
+			Application.Begin (Application.Top);
+
+			Assert.False (newAction);
+			Assert.False (copyAction);
+
+			Assert.False (menu.OnKeyDown (new (Key.AltMask, new KeyModifiers () { Alt = true })));
+			Assert.True (menu.OnKeyUp (new (Key.AltMask, new KeyModifiers () { Alt = true })));
+			Assert.True (menu.IsMenuOpen);
+			Application.Top.Redraw (Application.Top.Bounds);
+			var expected = @"
+ File  Edit
+";
+
+			var pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (1, 0, 11, 1), pos);
+
+			Assert.True (menu.ProcessKey (new (Key.N, null)));
+			Application.MainLoop.RunIteration ();
+			Assert.True (newAction);
+
+			Assert.True (menu.ProcessHotKey (new (Key.AltMask, new KeyModifiers () { Alt = true })));
+			Assert.True (menu.IsMenuOpen);
+			Application.Top.Redraw (Application.Top.Bounds);
+			expected = @"
+ File  Edit
+";
+
+			pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (1, 0, 11, 1), pos);
+
+			Assert.True (menu.ProcessKey (new (Key.CursorRight, null)));
+			Assert.True (menu.ProcessKey (new (Key.C, null)));
+			Application.MainLoop.RunIteration ();
+			Assert.True (copyAction);
+		}
 
 		// Defines the expected strings for a Menu. Currently supports 
 		//   - MenuBar with any number of MenuItems 
@@ -1852,5 +1852,205 @@ Edit
 │ Quit                       │
 └────────────────────────────┘", output);
 		}
+
+		[Fact, AutoInitShutdown]
+		public void Menu_With_Separator_Disabled_Border ()
+		{
+			var menu = new MenuBar (new MenuBarItem [] {
+				new MenuBarItem("File",new MenuItem [] {
+					new MenuItem("_Open", "Open a file", () => { }, null, null, Key.CtrlMask | Key.O),
+					null,
+					new MenuItem("_Quit","",null)
+				})
+			}) { MenusBorderStyle = LineStyle.None };
+
+			Application.Top.Add (menu);
+			Application.Begin (Application.Top);
+
+			menu.OpenMenu ();
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+ File                     
+ Open Open a file  Ctrl+O 
+──────────────────────────
+ Quit                     ", output);
+		}
+
+		[Fact, AutoInitShutdown]
+		public void DrawFrame_With_Positive_Positions_Disabled_Border ()
+		{
+			var menu = new MenuBar (new MenuBarItem [] {
+				new MenuBarItem (new MenuItem [] {
+					new MenuItem ("One", "", null),
+					new MenuItem ("Two", "", null)
+				})
+			}) { MenusBorderStyle = LineStyle.None };
+
+			Assert.Equal (Point.Empty, new Point (menu.Frame.X, menu.Frame.Y));
+
+			menu.OpenMenu ();
+			Application.Begin (Application.Top);
+
+			var expected = @"
+ One
+ Two
+";
+
+			_ = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
+		}
+
+		[Fact, AutoInitShutdown]
+		public void DrawFrame_With_Negative_Positions_Disabled_Border ()
+		{
+			var menu = new MenuBar (new MenuBarItem [] {
+				new MenuBarItem (new MenuItem [] {
+					new MenuItem ("One", "", null),
+					new MenuItem ("Two", "", null)
+				})
+			}) {
+				X = -2,
+				Y = -1,
+				MenusBorderStyle = LineStyle.None
+			};
+
+			Assert.Equal (new Point (-2, -1), new Point (menu.Frame.X, menu.Frame.Y));
+
+			menu.OpenMenu ();
+			Application.Begin (Application.Top);
+
+			var expected = @"
+ne
+wo
+";
+
+			_ = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
+
+			menu.CloseAllMenus ();
+			menu.Frame = new Rect (-2, -2, menu.Frame.Width, menu.Frame.Height);
+			menu.OpenMenu ();
+			Application.Refresh ();
+
+			expected = @"
+wo
+";
+
+			_ = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
+
+			menu.CloseAllMenus ();
+			menu.Frame = new Rect (0, 0, menu.Frame.Width, menu.Frame.Height);
+			((FakeDriver)Application.Driver).SetBufferSize (3, 2);
+			menu.OpenMenu ();
+			Application.Refresh ();
+
+			expected = @"
+ On
+ Tw
+";
+
+			_ = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
+
+			menu.CloseAllMenus ();
+			menu.Frame = new Rect (0, 0, menu.Frame.Width, menu.Frame.Height);
+			((FakeDriver)Application.Driver).SetBufferSize (3, 1);
+			menu.OpenMenu ();
+			Application.Refresh ();
+
+			expected = @"
+ Tw
+";
+
+			_ = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
+		}
+
+		[Fact, AutoInitShutdown]
+		public void UseSubMenusSingleFrame_False_Disabled_Border ()
+		{
+			var menu = new MenuBar (new MenuBarItem [] {
+				new MenuBarItem ("Numbers", new MenuItem [] {
+					new MenuItem ("One", "", null),
+					new MenuBarItem ("Two", new MenuItem [] {
+						new MenuItem ("Sub-Menu 1", "", null),
+						new MenuItem ("Sub-Menu 2", "", null)
+					}),
+					new MenuItem ("Three", "", null),
+				})
+			}) { MenusBorderStyle = LineStyle.None };
+			menu.UseKeysUpDownAsKeysLeftRight = true;
+			Application.Top.Add (menu);
+			Application.Begin (Application.Top);
+
+			Assert.Equal (Point.Empty, new Point (menu.Frame.X, menu.Frame.Y));
+			Assert.False (menu.UseSubMenusSingleFrame);
+
+			Assert.True (menu.ProcessHotKey (new KeyEvent (Key.F9, null)));
+			Application.Top.Redraw (Application.Top.Bounds);
+			var expected = @"
+ Numbers
+ One    
+ Two ►  
+ Three  
+";
+
+			_ = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
+
+			Assert.True (Application.Top.Subviews [1].ProcessKey (new KeyEvent (Key.CursorDown, null)));
+			Application.Top.Redraw (Application.Top.Bounds);
+			expected = @"
+ Numbers         
+ One             
+ Two ► Sub-Menu 1
+ Three Sub-Menu 2
+";
+
+			_ = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
+		}
+
+		[Fact, AutoInitShutdown]
+		public void UseSubMenusSingleFrame_True_Disabled_Border ()
+		{
+			var menu = new MenuBar (new MenuBarItem [] {
+				new MenuBarItem ("Numbers", new MenuItem [] {
+					new MenuItem ("One", "", null),
+					new MenuBarItem ("Two", new MenuItem [] {
+						new MenuItem ("Sub-Menu 1", "", null),
+						new MenuItem ("Sub-Menu 2", "", null)
+					}),
+					new MenuItem ("Three", "", null),
+				})
+			}) { MenusBorderStyle = LineStyle.None };
+
+			Application.Top.Add (menu);
+			Application.Begin (Application.Top);
+
+			Assert.Equal (Point.Empty, new Point (menu.Frame.X, menu.Frame.Y));
+			Assert.False (menu.UseSubMenusSingleFrame);
+			menu.UseSubMenusSingleFrame = true;
+			Assert.True (menu.UseSubMenusSingleFrame);
+
+
+			Assert.True (menu.ProcessHotKey (new KeyEvent (Key.F9, null)));
+			Application.Top.Redraw (Application.Top.Bounds);
+			var expected = @"
+ Numbers
+ One    
+ Two ►  
+ Three  
+";
+
+			_ = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
+
+			Assert.True (Application.Top.Subviews [1].ProcessKey (new KeyEvent (Key.CursorDown, null)));
+			Assert.True (Application.Top.Subviews [1].ProcessKey (new KeyEvent (Key.Enter, null)));
+			Application.Top.Redraw (Application.Top.Bounds);
+			expected = @"
+ Numbers   
+◄   Two    
+───────────
+ Sub-Menu 1
+ Sub-Menu 2
+";
+
+			_ = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
+		}
 	}
 }