浏览代码

Fixes #2558. MenuBar positions wrong in some situations. (#2567)

* Fixes #2558. MenuBar positions wrong in some situations.

* Replacing Application.Top with Application.Current.

* Fix typo.

* Fix shortcut tag overlapping help on smaller width and add more unit test.

* Resizing the console will close all opened menus.

* Resize first the console before show ContextMenu.

* Remove DriverFrame and DriverFrameOffset as not relevant.

* Replace _frame with Frame as requested.

* Fix xml document comment.

* Compare equality between Dialog and Application.Top.

* Move GetDriverLocationOffset and GetDriverLocationOffsetFromCurrent to the Menu.cs.

* Fix merge errors.

* Ensure menu is closed on click.

* Force Height always be 1 to avoid mouse events respond even outside bounds.

* Recovering UseSubMenusSingleFrame hope doesn't break again.

* Fix bugs and made requested changes.

---------

Co-authored-by: Tig <[email protected]>
BDisp 2 年之前
父节点
当前提交
915af9b3ff

+ 3 - 3
Terminal.Gui/Application.cs

@@ -1208,9 +1208,9 @@ namespace Terminal.Gui {
 
 		static void ProcessMouseEvent (MouseEvent me)
 		{
-			bool OutsideFrame (Point p, Rect r)
+			bool OutsideBounds (Point p, Rect r)
 			{
-				return p.X < 0 || p.X > r.Width - 1 || p.Y < 0 || p.Y > r.Height - 1;
+				return p.X < 0 || p.X > r.Right || p.Y < 0 || p.Y > r.Bottom;
 			}
 
 			if (IsMouseDisabled) {
@@ -1243,7 +1243,7 @@ namespace Terminal.Gui {
 					OfY = me.Y - newxy.Y,
 					View = view
 				};
-				if (OutsideFrame (new Point (nme.X, nme.Y), _mouseGrabView.Frame)) {
+				if (OutsideBounds (new Point (nme.X, nme.Y), _mouseGrabView.Bounds)) {
 					_lastMouseOwnerView?.OnMouseLeave (me);
 				}
 				//System.Diagnostics.Debug.WriteLine ($"{nme.Flags};{nme.X};{nme.Y};{mouseGrabView}");

+ 2 - 2
Terminal.Gui/View/View.cs

@@ -434,7 +434,7 @@ namespace Terminal.Gui {
 				}
 			}
 		}
-		
+
 		/// <summary>
 		/// Event fired when the <see cref="Visible"/> value is being changed.
 		/// </summary>
@@ -481,7 +481,7 @@ namespace Terminal.Gui {
 
 			return true;
 		}
-		
+
 		/// <summary>
 		/// Pretty prints the View
 		/// </summary>

+ 5 - 14
Terminal.Gui/Views/ContextMenu.cs

@@ -33,7 +33,7 @@ namespace Terminal.Gui {
 		public ContextMenu () : this (0, 0, new MenuBarItem ()) { }
 
 		/// <summary>
-		/// Initializes a context menu, with a <see cref="View"/> specifiying the parent/hose of the menu.
+		/// Initializes a context menu, with a <see cref="View"/> specifying the parent/host of the menu.
 		/// </summary>
 		/// <param name="host">The host view.</param>
 		/// <param name="menuItems">The menu items for the context menu.</param>
@@ -79,7 +79,6 @@ namespace Terminal.Gui {
 			}
 			if (container != null) {
 				container.Closing -= Container_Closing;
-				container.TerminalResized -= Container_Resized;
 			}
 		}
 
@@ -91,13 +90,12 @@ namespace Terminal.Gui {
 			if (menuBar != null) {
 				Hide ();
 			}
-			container = Application.Top;
+			container = Application.Current;
 			container.Closing += Container_Closing;
-			container.TerminalResized += Container_Resized;
-			var frame = container.Frame;
+			var frame = new Rect (0, 0, View.Driver.Cols, View.Driver.Rows);
 			var position = Position;
 			if (Host != null) {
-				Host.ViewToScreen (container.Frame.X, container.Frame.Y, out int x, out int y);
+				Host.ViewToScreen (frame.X, frame.Y, out int x, out int y);
 				var pos = new Point (x, y);
 				pos.Y += Host.Frame.Height - 1;
 				if (position != pos) {
@@ -119,7 +117,7 @@ namespace Terminal.Gui {
 					if (Host == null) {
 						position.Y = frame.Bottom - rect.Height - 1;
 					} else {
-						Host.ViewToScreen (container.Frame.X, container.Frame.Y, out int x, out int y);
+						Host.ViewToScreen (frame.X, frame.Y, out int x, out int y);
 						var pos = new Point (x, y);
 						position.Y = pos.Y - rect.Height - 1;
 					}
@@ -145,13 +143,6 @@ namespace Terminal.Gui {
 			menuBar.OpenMenu ();
 		}
 
-		private void Container_Resized (object sender, SizeChangedEventArgs e)
-		{
-			if (IsShow) {
-				Show ();
-			}
-		}
-
 		private void Container_Closing (object sender, ToplevelClosingEventArgs obj)
 		{
 			Hide ();

+ 130 - 40
Terminal.Gui/Views/Menu.cs

@@ -202,7 +202,7 @@ namespace Terminal.Gui {
 		/// Gets the parent for this <see cref="MenuItem"/>.
 		/// </summary>
 		/// <value>The parent.</value>
-		public MenuItem Parent { get; internal set; }
+		public MenuItem Parent { get; set; }
 
 		/// <summary>
 		/// Gets if this <see cref="MenuItem"/> is from a sub-menu.
@@ -443,7 +443,7 @@ namespace Terminal.Gui {
 			}
 			int minX = x;
 			int minY = y;
-			var borderOffset = border != LineStyle.None ? 2 : 0; // This 2 is frame border?
+			var borderOffset = 2; // This 2 is for the space around
 			int maxW = (items.Max (z => z?.Width) ?? 0) + borderOffset;
 			int maxH = items.Length + borderOffset;
 			if (parent != null && x + maxW > Driver.Cols) {
@@ -482,6 +482,7 @@ namespace Terminal.Gui {
 
 			if (Application.Current != null) {
 				Application.Current.DrawContentComplete += Current_DrawContentComplete;
+				Application.Current.TerminalResized += Current_TerminalResized;
 			}
 			Application.RootMouseEvent += Application_RootMouseEvent;
 
@@ -510,10 +511,40 @@ namespace Terminal.Gui {
 			AddKeyBinding (Key.Enter, Command.Accept);
 		}
 
+		private void Current_TerminalResized (object sender, SizeChangedEventArgs e)
+		{
+			if (host.IsMenuOpen) {
+				host.CloseAllMenus ();
+			}
+		}
+
+		/// <inheritdoc/>
+		public override void OnVisibleChanged ()
+		{
+			base.OnVisibleChanged ();
+			if (Visible) {
+				Application.RootMouseEvent += Application_RootMouseEvent;
+			} else {
+				Application.RootMouseEvent -= Application_RootMouseEvent;
+			}
+		}
+
 		private void Application_RootMouseEvent (MouseEvent me)
 		{
-			var view = View.FindDeepestView (this, me.X, me.Y, out int rx, out int ry);
+			if (me.View is MenuBar) {
+				return;
+			}
+			var locationOffset = host.GetScreenOffsetFromCurrent ();
+			if (SuperView != null && SuperView != Application.Current) {
+				locationOffset.X += SuperView.Border.Thickness.Left;
+				locationOffset.Y += SuperView.Border.Thickness.Top;
+			}
+			var view = View.FindDeepestView (this, me.X + locationOffset.X, me.Y + locationOffset.Y, out int rx, out int ry);
 			if (view == this) {
+				if (!Visible) {
+					throw new InvalidOperationException ("This shouldn't running on a invisible menu!");
+				}
+
 				var nme = new MouseEvent () {
 					X = rx,
 					Y = ry,
@@ -540,8 +571,8 @@ namespace Terminal.Gui {
 			if (barItems.Children == null) {
 				return;
 			}
-			var savedClip = Application.Driver.Clip;
-			Application.Driver.Clip = Application.Top.Frame;
+			var savedClip = Driver.Clip;
+			Driver.Clip = new Rect (0, 0, Driver.Cols, Driver.Rows);
 
 			Driver.SetAttribute (GetNormalColor ());
 
@@ -549,8 +580,12 @@ namespace Terminal.Gui {
 			OnRenderLineCanvas ();
 
 			for (int i = Bounds.Y; i < barItems.Children.Length; i++) {
-				if (i < 0)
+				if (i < 0) {
 					continue;
+				}
+				if (ViewToScreen (Bounds).Y + i >= Driver.Rows) {
+					break;
+				}
 				var item = barItems.Children [i];
 				Driver.SetAttribute (item == null ? GetNormalColor ()
 					: i == current ? ColorScheme.Focus : GetNormalColor ());
@@ -563,8 +598,12 @@ namespace Terminal.Gui {
 
 				Driver.SetAttribute (DetermineColorSchemeFor (item, i));
 				for (int p = Bounds.X; p < Frame.Width - 2; p++) { // This - 2 is for the border
-					if (p < 0)
+					if (p < 0) {
 						continue;
+					}
+					if (ViewToScreen (Bounds).X + p >= Driver.Cols) {
+						break;
+					}
 					if (item == null)
 						Driver.AddRune (Driver.HLine);
 					else if (i == 0 && p == 0 && host.UseSubMenusSingleFrame && item.Parent.Parent != null)
@@ -605,9 +644,9 @@ namespace Terminal.Gui {
 					textToDraw = item.Title;
 				}
 
-				ViewToScreen (0, i, out int vtsCol, out _, false);
+				ViewToScreen (0, i, out int vtsCol, out int vtsRow, false);
 				if (vtsCol < Driver.Cols) {
-					Move (1, i);
+					Driver.Move (vtsCol + 1, vtsRow);
 					if (!item.IsEnabled ()) {
 						DrawHotString (textToDraw, ColorScheme.Disabled, ColorScheme.Disabled);
 					} else if (i == 0 && host.UseSubMenusSingleFrame && item.Parent.Parent != null) {
@@ -630,15 +669,14 @@ namespace Terminal.Gui {
 					// The help string
 					var l = item.ShortcutTag.ConsoleWidth == 0 ? item.Help.ConsoleWidth : item.Help.ConsoleWidth + item.ShortcutTag.ConsoleWidth + 2;
 					var col = Frame.Width - l - 3;
-					ViewToScreen (col, i, out vtsCol, out _, false);
+					ViewToScreen (col, i, out vtsCol, out vtsRow, false);
 					if (vtsCol < Driver.Cols) {
-						Move (col, i);
+						Driver.Move (vtsCol, vtsRow);
 						Driver.AddStr (item.Help);
 
 						// The shortcut tag string
 						if (!item.ShortcutTag.IsEmpty) {
-							l = item.ShortcutTag.ConsoleWidth;
-							Move (Frame.Width - l - 3, i);
+							Driver.Move (vtsCol + l - item.ShortcutTag.ConsoleWidth, vtsRow);
 							Driver.AddStr (item.ShortcutTag);
 						}
 					}
@@ -651,7 +689,9 @@ namespace Terminal.Gui {
 
 		private void Current_DrawContentComplete (object sender, DrawEventArgs e)
 		{
-			OnDrawContent (Bounds);
+			if (Visible) {
+				OnDrawContent (Bounds);
+			}
 		}
 
 		public override void PositionCursor ()
@@ -858,12 +898,11 @@ namespace Terminal.Gui {
 			}
 			host.handled = false;
 			bool disabled;
-			var meYOffset = BorderStyle != LineStyle.None ? 1 : 0;
+			var meY = me.Y - (Border == null ? 0 : Border.Thickness.Top);
 			if (me.Flags == MouseFlags.Button1Clicked) {
 				disabled = false;
-				if (me.Y < meYOffset)
+				if (meY < 0)
 					return true;
-				var meY = me.Y - meYOffset;
 				if (meY >= barItems.Children.Length)
 					return true;
 				var item = barItems.Children [meY];
@@ -878,14 +917,14 @@ namespace Terminal.Gui {
 				me.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition)) {
 
 				disabled = false;
-				if (me.Y < meYOffset || me.Y - meYOffset >= barItems.Children.Length) {
+				if (meY < 0 || meY >= barItems.Children.Length) {
 					return true;
 				}
-				var item = barItems.Children [me.Y - meYOffset];
+				var item = barItems.Children [meY];
 				if (item == null) return true;
 				if (item == null || !item.IsEnabled ()) disabled = true;
 				if (item != null && !disabled)
-					current = me.Y - meYOffset;
+					current = meY;
 				if (host.UseSubMenusSingleFrame || !CheckSubMenu ()) {
 					SetNeedsDisplay ();
 					SetParentSetNeedsDisplay ();
@@ -950,6 +989,7 @@ namespace Terminal.Gui {
 		{
 			if (Application.Current != null) {
 				Application.Current.DrawContentComplete -= Current_DrawContentComplete;
+				Application.Current.TerminalResized -= Current_TerminalResized;
 			}
 			Application.RootMouseEvent -= Application_RootMouseEvent;
 			base.Dispose (disposing);
@@ -1284,7 +1324,7 @@ namespace Terminal.Gui {
 			set {
 				if (ocm != value) {
 					ocm = value;
-					if (ocm.current > -1) {
+					if (ocm != null && ocm.current > -1) {
 						OnMenuOpened ();
 					}
 				}
@@ -1381,7 +1421,7 @@ namespace Terminal.Gui {
 				if (openSubMenu != null && !CloseMenu (false, true))
 					return;
 				if (openMenu != null) {
-					Application.Top.Remove (openMenu);
+					Application.Current.Remove (openMenu);
 					openMenu.Dispose ();
 					openMenu = null;
 				}
@@ -1390,18 +1430,21 @@ namespace Terminal.Gui {
 				// text belonging to the menu 
 				for (int i = 0; i < index; i++)
 					pos += Menus [i].TitleLength + (Menus [i].Help.ConsoleWidth > 0 ? Menus [i].Help.ConsoleWidth + 2 : 0) + leftPadding + rightPadding;
-				var superView = SuperView == null ? Application.Top : SuperView;
-				Point locationOffset;
-				if (superView.BorderStyle != LineStyle.None) {
-					locationOffset = new Point (superView.Frame.X + 1, superView.Frame.Y + 1);
-				} else {
-					locationOffset = new Point (superView.Frame.X, superView.Frame.Y);
+
+				var locationOffset = Point.Empty;
+				// if SuperView is null then it's from a ContextMenu
+				if (SuperView == null) {
+					locationOffset = GetScreenOffset ();
+				}
+				if (SuperView != null && SuperView != Application.Current) {
+					locationOffset.X += SuperView.Border.Thickness.Left;
+					locationOffset.Y += SuperView.Border.Thickness.Top;
 				}
 				openMenu = new Menu (this, Frame.X + pos + locationOffset.X, Frame.Y + 1 + locationOffset.Y, Menus [index], null, MenusBorderStyle);
 				openCurrentMenu = openMenu;
 				openCurrentMenu.previousSubFocused = openMenu;
 
-				Application.Top.Add (openMenu);
+				Application.Current.Add (openMenu);
 				openMenu.SetFocus ();
 				break;
 			default:
@@ -1417,21 +1460,21 @@ namespace Terminal.Gui {
 						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;
+						// 2 is for the parent and the separator
 						var mbi = new MenuItem [2 + subMenu.Children.Length];
 						mbi [0] = new MenuItem () { Title = subMenu.Title, Parent = subMenu };
 						mbi [1] = null;
 						for (int j = 0; j < subMenu.Children.Length; j++) {
 							mbi [j + 2] = subMenu.Children [j];
 						}
-						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, null, MenusBorderStyle);
+						var newSubMenu = new MenuBarItem (mbi) { Parent = subMenu };
+						openCurrentMenu = new Menu (this, first.Frame.Left, first.Frame.Top, newSubMenu, null, MenusBorderStyle);
 						last.Visible = false;
 						Application.GrabMouse (openCurrentMenu);
 					}
 					openCurrentMenu.previousSubFocused = last.previousSubFocused;
 					openSubMenu.Add (openCurrentMenu);
-					Application.Top.Add (openCurrentMenu);
+					Application.Current.Add (openCurrentMenu);
 				}
 				selectedSub = openSubMenu.Count - 1;
 				if (selectedSub > -1 && SelectEnabledItem (openCurrentMenu.barItems.Children, openCurrentMenu.current, out openCurrentMenu.current)) {
@@ -1562,7 +1605,7 @@ namespace Terminal.Gui {
 			switch (isSubMenu) {
 			case false:
 				if (openMenu != null) {
-					Application.Top.Remove (openMenu);
+					Application.Current.Remove (openMenu);
 				}
 				SetNeedsDisplay ();
 				if (previousFocused != null && previousFocused is Menu && openMenu != null && previousFocused.ToString () != openCurrentMenu.ToString ())
@@ -1578,6 +1621,14 @@ namespace Terminal.Gui {
 					if (!reopen) {
 						selected = -1;
 					}
+					if (openSubMenu != null) {
+						openSubMenu = null;
+					}
+					if (openCurrentMenu != null) {
+						Application.Current.Remove (openCurrentMenu);
+						openCurrentMenu.Dispose ();
+						openCurrentMenu = null;
+					}
 					LastFocused.SetFocus ();
 				} else if (openSubMenu == null || openSubMenu.Count == 0) {
 					CloseAllMenus ();
@@ -1621,7 +1672,7 @@ namespace Terminal.Gui {
 				openCurrentMenu.SetFocus ();
 				if (openSubMenu != null) {
 					menu = openSubMenu [i];
-					Application.Top.Remove (menu);
+					Application.Current.Remove (menu);
 					openSubMenu.Remove (menu);
 					menu.Dispose ();
 				}
@@ -1637,7 +1688,7 @@ namespace Terminal.Gui {
 		{
 			if (openSubMenu != null) {
 				foreach (var item in openSubMenu) {
-					Application.Top.Remove (item);
+					Application.Current.Remove (item);
 					item.Dispose ();
 				}
 			}
@@ -1646,7 +1697,7 @@ namespace Terminal.Gui {
 		internal void CloseAllMenus ()
 		{
 			if (!isMenuOpening && !isMenuClosing) {
-				if (openSubMenu != null && !CloseMenu (false, true))
+				if (openSubMenu != null && !CloseMenu (false, true, true))
 					return;
 				if (!CloseMenu (false))
 					return;
@@ -1923,7 +1974,12 @@ namespace Terminal.Gui {
 				(me.Flags == MouseFlags.ReportMousePosition && selected > -1) ||
 				(me.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition) && selected > -1)) {
 				int pos = xOrigin;
-				int cx = me.X;
+				Point locationOffset = default;
+				if (SuperView != null) {
+					locationOffset.X += SuperView.Border.Thickness.Left;
+					locationOffset.Y += SuperView.Border.Thickness.Top;
+				}
+				int cx = me.X - locationOffset.X;
 				for (int i = 0; i < Menus.Length; i++) {
 					if (cx >= pos && cx < pos + leftPadding + Menus [i].TitleLength + Menus [i].Help.ConsoleWidth + rightPadding) {
 						if (me.Flags == MouseFlags.Button1Clicked) {
@@ -1949,11 +2005,19 @@ namespace Terminal.Gui {
 								}
 								Activate (i);
 							}
-						} else {
-							if (IsMenuOpen)
+						} else if (IsMenuOpen) {
+							if (!UseSubMenusSingleFrame || (UseSubMenusSingleFrame && openCurrentMenu != null
+								&& openCurrentMenu.barItems.Parent != null && openCurrentMenu.barItems.Parent.Parent != Menus [i])) {
+
 								Activate (i);
+							}
 						}
 						return true;
+					} else if (i == Menus.Length - 1 && me.Flags == MouseFlags.Button1Clicked) {
+						if (IsMenuOpen && !Menus [i].IsTopLevel) {
+							CloseAllMenus ();
+							return true;
+						}
 					}
 					pos += leftPadding + Menus [i].TitleLength + rightPadding;
 				}
@@ -2065,5 +2129,31 @@ namespace Terminal.Gui {
 
 			return base.OnEnter (view);
 		}
+
+		/// <summary>
+		/// Gets the superview location offset relative to the <see cref="ConsoleDriver"/> location.
+		/// </summary>
+		/// <returns>The location offset.</returns>
+		internal Point GetScreenOffset ()
+		{
+			var superViewFrame = SuperView == null ? new Rect (0, 0, Driver.Cols, Driver.Rows) : SuperView.Frame;
+			var sv = SuperView == null ? Application.Current : SuperView;
+			var boundsOffset = sv.GetBoundsOffset ();
+			return new Point (superViewFrame.X - sv.Frame.X - boundsOffset.X,
+				superViewFrame.Y - sv.Frame.Y - boundsOffset.Y);
+		}
+
+		/// <summary>
+		/// Gets the <see cref="Application.Current"/> location offset relative to the <see cref="ConsoleDriver"/> location.
+		/// </summary>
+		/// <returns>The location offset.</returns>
+		internal Point GetScreenOffsetFromCurrent ()
+		{
+			var screen = new Rect (0, 0, Driver.Cols, Driver.Rows);
+			var currentFrame = Application.Current.Frame;
+			var boundsOffset = Application.Top.GetBoundsOffset ();
+			return new Point (screen.X - currentFrame.X - boundsOffset.X
+				, screen.Y - currentFrame.Y - boundsOffset.Y);
+		}
 	}
 }

+ 6 - 1
Terminal.Gui/Views/TextField.cs

@@ -293,7 +293,12 @@ namespace Terminal.Gui {
 		public override Rect Frame {
 			get => base.Frame;
 			set {
-				base.Frame = value;
+				if (value.Height > 1) {
+					base.Frame = new Rect(value.X, value.Y, value.Width, 1);
+					Height = 1;
+				} else {
+					base.Frame = value;
+				}
 				Adjust ();
 			}
 		}

+ 21 - 21
UICatalog/Scenarios/DynamicMenuBar.cs

@@ -208,7 +208,7 @@ namespace UICatalog.Scenarios {
 				};
 				Add (_frmMenuDetails);
 
-				_btnMenuBarUp.Clicked += (s,e) => {
+				_btnMenuBarUp.Clicked += (s, e) => {
 					var i = _currentSelectedMenuBar;
 					var menuItem = _menuBar != null && _menuBar.Menus.Length > 0 ? _menuBar.Menus [i] : null;
 					if (menuItem != null) {
@@ -222,7 +222,7 @@ namespace UICatalog.Scenarios {
 					}
 				};
 
-				_btnMenuBarDown.Clicked += (s,e) => {
+				_btnMenuBarDown.Clicked += (s, e) => {
 					var i = _currentSelectedMenuBar;
 					var menuItem = _menuBar != null && _menuBar.Menus.Length > 0 ? _menuBar.Menus [i] : null;
 					if (menuItem != null) {
@@ -236,7 +236,7 @@ namespace UICatalog.Scenarios {
 					}
 				};
 
-				_btnUp.Clicked += (s,e) => {
+				_btnUp.Clicked += (s, e) => {
 					var i = _lstMenus.SelectedItem;
 					var menuItem = DataContext.Menus.Count > 0 ? DataContext.Menus [i].MenuItem : null;
 					if (menuItem != null) {
@@ -251,7 +251,7 @@ namespace UICatalog.Scenarios {
 					}
 				};
 
-				_btnDown.Clicked += (s,e) => {
+				_btnDown.Clicked += (s, e) => {
 					var i = _lstMenus.SelectedItem;
 					var menuItem = DataContext.Menus.Count > 0 ? DataContext.Menus [i].MenuItem : null;
 					if (menuItem != null) {
@@ -266,7 +266,7 @@ namespace UICatalog.Scenarios {
 					}
 				};
 
-				_btnPreviowsParent.Clicked += (s,e) => {
+				_btnPreviowsParent.Clicked += (s, e) => {
 					if (_currentMenuBarItem != null && _currentMenuBarItem.Parent != null) {
 						var mi = _currentMenuBarItem;
 						_currentMenuBarItem = _currentMenuBarItem.Parent as MenuBarItem;
@@ -295,16 +295,16 @@ namespace UICatalog.Scenarios {
 					X = Pos.Right (_btnOk) + 3,
 					Y = Pos.Top (_btnOk),
 				};
-				_btnCancel.Clicked += (s,e) => {
+				_btnCancel.Clicked += (s, e) => {
 					SetFrameDetails (_currentEditMenuBarItem);
 				};
 				Add (_btnCancel);
 
-				_lstMenus.SelectedItemChanged += (s,e) => {
+				_lstMenus.SelectedItemChanged += (s, e) => {
 					SetFrameDetails ();
 				};
 
-				_btnOk.Clicked += (s,e) => {
+				_btnOk.Clicked += (s, e) => {
 					if (ustring.IsNullOrEmpty (_frmMenuDetails._txtTitle.Text) && _currentEditMenuBarItem != null) {
 						MessageBox.ErrorQuery ("Invalid title", "Must enter a valid title!.", "Ok");
 					} else if (_currentEditMenuBarItem != null) {
@@ -320,7 +320,7 @@ namespace UICatalog.Scenarios {
 					}
 				};
 
-				_btnAdd.Clicked += (s,e) => {
+				_btnAdd.Clicked += (s, e) => {
 					if (MenuBar == null) {
 						MessageBox.ErrorQuery ("Menu Bar Error", "Must add a MenuBar first!", "Ok");
 						_btnAddMenuBar.SetFocus ();
@@ -357,7 +357,7 @@ namespace UICatalog.Scenarios {
 					}
 				};
 
-				_btnRemove.Clicked += (s,e) => {
+				_btnRemove.Clicked += (s, e) => {
 					var menuItem = DataContext.Menus.Count > 0 ? DataContext.Menus [_lstMenus.SelectedItem].MenuItem : null;
 					if (menuItem != null) {
 						var childrens = ((MenuBarItem)_currentMenuBarItem).Children;
@@ -389,7 +389,7 @@ namespace UICatalog.Scenarios {
 					}
 				};
 
-				_lstMenus.OpenSelectedItem += (s,e) => {
+				_lstMenus.OpenSelectedItem += (s, e) => {
 					_currentMenuBarItem = DataContext.Menus [e.Item].MenuItem;
 					if (!(_currentMenuBarItem is MenuBarItem)) {
 						MessageBox.ErrorQuery ("Menu Open Error", "Must allows sub menus first!", "Ok");
@@ -403,18 +403,18 @@ namespace UICatalog.Scenarios {
 				};
 
 				_lstMenus.Enter += (s, e) => {
-					var menuBarItem = DataContext.Menus.Count > 0 ? DataContext.Menus [_lstMenus.SelectedItem].MenuItem : null;
+					var menuBarItem = _lstMenus.SelectedItem > -1 && DataContext.Menus.Count > 0 ? DataContext.Menus [_lstMenus.SelectedItem].MenuItem : null;
 					SetFrameDetails (menuBarItem);
 				};
 
-				_btnNext.Clicked += (s,e) => {
+				_btnNext.Clicked += (s, e) => {
 					if (_menuBar != null && _currentSelectedMenuBar + 1 < _menuBar.Menus.Length) {
 						_currentSelectedMenuBar++;
 					}
 					SelectCurrentMenuBarItem ();
 				};
 
-				_btnPrevious.Clicked += (s,e) => {
+				_btnPrevious.Clicked += (s, e) => {
 					if (_currentSelectedMenuBar - 1 > -1) {
 						_currentSelectedMenuBar--;
 					}
@@ -428,7 +428,7 @@ namespace UICatalog.Scenarios {
 					}
 				};
 
-				_btnAddMenuBar.Clicked += (s,e) => {
+				_btnAddMenuBar.Clicked += (s, e) => {
 					var frameDetails = new DynamicMenuBarDetails (null, false);
 					var item = frameDetails.EnterMenuItem ();
 					if (item == null) {
@@ -455,7 +455,7 @@ namespace UICatalog.Scenarios {
 					_menuBar.SetNeedsDisplay ();
 				};
 
-				_btnRemoveMenuBar.Clicked += (s,e) => {
+				_btnRemoveMenuBar.Clicked += (s, e) => {
 					if (_menuBar == null || _menuBar.Menus.Length == 0) {
 						return;
 					}
@@ -505,7 +505,7 @@ namespace UICatalog.Scenarios {
 					MenuItem menuItem;
 
 					if (menuBarItem == null) {
-						menuItem = DataContext.Menus.Count > 0 ? DataContext.Menus [_lstMenus.SelectedItem].MenuItem : null;
+						menuItem = _lstMenus.SelectedItem > -1 && DataContext.Menus.Count > 0 ? DataContext.Menus [_lstMenus.SelectedItem].MenuItem : null;
 					} else {
 						menuItem = menuBarItem;
 					}
@@ -778,7 +778,7 @@ namespace UICatalog.Scenarios {
 					X = Pos.X (_lblShortcut),
 					Y = Pos.Bottom (_txtShortcut) + 1
 				};
-				_btnShortcut.Clicked += (s,e) => {
+				_btnShortcut.Clicked += (s, e) => {
 					_txtShortcut.Text = "";
 				};
 				Add (_btnShortcut);
@@ -829,7 +829,7 @@ namespace UICatalog.Scenarios {
 						_txtShortcut.Enabled = _ckbIsTopLevel.Checked == false && _ckbSubMenu.Checked == false;
 					}
 				};
-				_ckbNullCheck.Toggled += (s,e) => {
+				_ckbNullCheck.Toggled += (s, e) => {
 					if (_menuItem != null) {
 						_menuItem.AllowNullChecked = (bool)_ckbNullCheck.Checked;
 					}
@@ -861,7 +861,7 @@ namespace UICatalog.Scenarios {
 				var _btnOk = new Button ("Ok") {
 					IsDefault = true,
 				};
-				_btnOk.Clicked += (s,e) => {
+				_btnOk.Clicked += (s, e) => {
 					if (ustring.IsNullOrEmpty (_txtTitle.Text)) {
 						MessageBox.ErrorQuery ("Invalid title", "Must enter a valid title!.", "Ok");
 					} else {
@@ -870,7 +870,7 @@ namespace UICatalog.Scenarios {
 					}
 				};
 				var _btnCancel = new Button ("Cancel");
-				_btnCancel.Clicked += (s,e) => {
+				_btnCancel.Clicked += (s, e) => {
 					_txtTitle.Text = ustring.Empty;
 					Application.RequestStop ();
 				};

+ 2 - 1
UICatalog/Scenarios/Text.cs

@@ -21,6 +21,7 @@ namespace UICatalog.Scenarios {
 				X = 1,
 				Y = 0,
 				Width = Dim.Percent (50) - 1,
+				// Height will be replaced with 1
 				Height = 2
 			};
 
@@ -51,7 +52,7 @@ namespace UICatalog.Scenarios {
 			// TextView is a rich (as in functionality, not formatting) text editing control
 			var textView = new TextView () {
 				X = 1,
-				Y = Pos.Bottom (textField),
+				Y = Pos.Bottom (textField) + 1,
 				Width = Dim.Percent (50) - 1,
 				Height = Dim.Percent (30),
 			};

+ 26 - 2
UICatalog/UICatalog.cs

@@ -250,6 +250,7 @@ namespace UICatalog {
 		/// the command line) and each time a Scenario ends.
 		/// </summary>
 		public class UICatalogTopLevel : Toplevel {
+			public MenuItem? miUseSubMenusSingleFrame;
 			public MenuItem? miIsMenuBorderDisabled;
 			public MenuItem? miIsMouseDisabled;
 			public MenuItem? miEnableConsoleScrolling;
@@ -491,18 +492,36 @@ namespace UICatalog {
 					CreateEnableConsoleScrollingMenuItems (),
 					CreateDisabledEnabledMouseItems (),
 					CreateDisabledEnabledMenuBorder (),
+					CreateDisabledEnableUseSubMenusSingleFrame (),
 					CreateKeybindingsMenuItems ()
 				};
 				return menuItems;
 			}
 
+			MenuItem [] CreateDisabledEnableUseSubMenusSingleFrame ()
+			{
+				List<MenuItem> menuItems = new List<MenuItem> ();
+				miUseSubMenusSingleFrame = new MenuItem {
+					Title = "Enable _Sub-Menus Single Frame"
+				};
+				miUseSubMenusSingleFrame.Shortcut = Key.CtrlMask | Key.AltMask | (Key)miUseSubMenusSingleFrame!.Title!.ToString ()!.Substring (8, 1) [0];
+				miUseSubMenusSingleFrame.CheckType |= MenuItemCheckStyle.Checked;
+				miUseSubMenusSingleFrame.Action += () => {
+					miUseSubMenusSingleFrame.Checked = (bool)!miUseSubMenusSingleFrame.Checked!;
+					MenuBar.UseSubMenusSingleFrame = (bool)miUseSubMenusSingleFrame.Checked;
+				};
+				menuItems.Add (miUseSubMenusSingleFrame);
+
+				return menuItems.ToArray ();
+			}
+
 			MenuItem [] CreateDisabledEnabledMenuBorder ()
 			{
 				List<MenuItem> menuItems = new List<MenuItem> ();
 				miIsMenuBorderDisabled = new MenuItem {
-					Title = "Disable _Menu Border"
+					Title = "Disable Menu _Border"
 				};
-				miIsMenuBorderDisabled.Shortcut = Key.CtrlMask | Key.AltMask | (Key)miIsMenuBorderDisabled!.Title!.ToString ()!.Substring (1, 1) [0];
+				miIsMenuBorderDisabled.Shortcut = Key.CtrlMask | Key.AltMask | (Key)miIsMenuBorderDisabled!.Title!.ToString ()!.Substring (14, 1) [0];
 				miIsMenuBorderDisabled.CheckType |= MenuItemCheckStyle.Checked;
 				miIsMenuBorderDisabled.Action += () => {
 					miIsMenuBorderDisabled.Checked = (bool)!miIsMenuBorderDisabled.Checked!;
@@ -709,6 +728,11 @@ namespace UICatalog {
 
 				_themeMenuItems = ((UICatalogTopLevel)Application.Top).CreateThemeMenuItems ();
 				_themeMenuBarItem!.Children = _themeMenuItems;
+				foreach (var mi in _themeMenuItems!) {
+					if (mi != null && mi.Parent == null) {
+						mi.Parent = _themeMenuBarItem;
+					}
+				}
 
 				var checkedThemeMenu = _themeMenuItems?.Where (m => m?.Checked ?? false).FirstOrDefault ();
 				if (checkedThemeMenu != null) {

+ 254 - 3
UnitTests/View/NavigationTests.cs

@@ -46,7 +46,7 @@ namespace Terminal.Gui.ViewTests {
 			top.ProcessKey (new KeyEvent (Key.BackTab | Key.ShiftMask, new KeyModifiers ()));
 			Assert.Equal ($"WindowSubview", top.MostFocused.Text);
 		}
-		
+
 
 		[Fact]
 		public void Subviews_TabIndexes_AreEqual ()
@@ -949,7 +949,7 @@ namespace Terminal.Gui.ViewTests {
 			// Assert does Not throw NullReferenceException
 			top.SetFocus ();
 		}
-		
+
 		[Fact, AutoInitShutdown]
 		public void SetHasFocus_Do_Not_Throws_If_OnLeave_Remove_Focused_Changing_To_Null ()
 		{
@@ -982,7 +982,7 @@ namespace Terminal.Gui.ViewTests {
 			Assert.True (subView1Leave);
 			Assert.False (subView1subView1Leave);
 		}
-		
+
 		[Fact, AutoInitShutdown]
 		public void Remove_Does_Not_Change_Focus ()
 		{
@@ -1078,5 +1078,256 @@ namespace Terminal.Gui.ViewTests {
 			Assert.True (removed);
 			Assert.Null (view3);
 		}
+
+		[Fact, AutoInitShutdown]
+		public void ScreenToView_ViewToScreen_FindDeepestView_Full_Top ()
+		{
+			var top = Application.Current;
+			top.BorderStyle = LineStyle.Single;
+			var view = new View () { X = 3, Y = 2, Width = 10, Height = 1, Text = "0123456789" };
+			top.Add (view);
+
+			Application.Begin (top);
+
+			Assert.Equal (Application.Current, top);
+			Assert.Equal (new Rect (0, 0, 80, 25), new Rect (0, 0, View.Driver.Cols, View.Driver.Rows));
+			Assert.Equal (new Rect (0, 0, View.Driver.Cols, View.Driver.Rows), top.Frame);
+			Assert.Equal (new Rect (0, 0, 80, 25), top.Frame);
+
+			((FakeDriver)Application.Driver).SetBufferSize (20, 10);
+			Assert.Equal (new Rect (0, 0, View.Driver.Cols, View.Driver.Rows), top.Frame);
+			Assert.Equal (new Rect (0, 0, 20, 10), top.Frame);
+			_ = TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌──────────────────┐
+│                  │
+│                  │
+│   0123456789     │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+└──────────────────┘", output);
+
+			// top
+			Assert.Equal (Point.Empty, top.ScreenToView (0, 0));
+			top.Margin.ViewToScreen (0, 0, out int col, out int row);
+			Assert.Equal (0, col);
+			Assert.Equal (0, row);
+			top.Border.ViewToScreen (0, 0, out col, out row);
+			Assert.Equal (0, col);
+			Assert.Equal (0, row);
+			top.Padding.ViewToScreen (0, 0, out col, out row);
+			Assert.Equal (0, col);
+			Assert.Equal (0, row);
+			top.ViewToScreen (0, 0, out col, out row);
+			Assert.Equal (1, col);
+			Assert.Equal (1, row);
+			top.ViewToScreen (-1, -1, out col, out row);
+			Assert.Equal (0, col);
+			Assert.Equal (0, row);
+			Assert.Equal (top, View.FindDeepestView (top, 0, 0, out int rx, out int ry));
+			Assert.Equal (0, rx);
+			Assert.Equal (0, ry);
+			Assert.Equal (new Point (3, 2), top.ScreenToView (3, 2));
+			top.ViewToScreen (3, 2, out col, out row);
+			Assert.Equal (4, col);
+			Assert.Equal (3, row);
+			Assert.Equal (view, View.FindDeepestView (top, col, row, out rx, out ry));
+			Assert.Equal (0, rx);
+			Assert.Equal (0, ry);
+			Assert.Equal (top, View.FindDeepestView (top, 3, 2, out rx, out ry));
+			Assert.Equal (3, rx);
+			Assert.Equal (2, ry);
+			Assert.Equal (new Point (13, 2), top.ScreenToView (13, 2));
+			top.ViewToScreen (12, 2, out col, out row);
+			Assert.Equal (13, col);
+			Assert.Equal (3, row);
+			Assert.Equal (view, View.FindDeepestView (top, col, row, out rx, out ry));
+			Assert.Equal (9, rx);
+			Assert.Equal (0, ry);
+			top.ViewToScreen (13, 2, out col, out row);
+			Assert.Equal (14, col);
+			Assert.Equal (3, row);
+			Assert.Equal (top, View.FindDeepestView (top, 13, 2, out rx, out ry));
+			Assert.Equal (13, rx);
+			Assert.Equal (2, ry);
+			Assert.Equal (new Point (14, 3), top.ScreenToView (14, 3));
+			top.ViewToScreen (14, 3, out col, out row);
+			Assert.Equal (15, col);
+			Assert.Equal (4, row);
+			Assert.Equal (top, View.FindDeepestView (top, 14, 3, out rx, out ry));
+			Assert.Equal (14, rx);
+			Assert.Equal (3, ry);
+			// view
+			Assert.Equal (new Point (-4, -3), view.ScreenToView (0, 0));
+			view.Margin.ViewToScreen (-3, -2, out col, out row);
+			Assert.Equal (1, col);
+			Assert.Equal (1, row);
+			view.Border.ViewToScreen (-3, -2, out col, out row);
+			Assert.Equal (1, col);
+			Assert.Equal (1, row);
+			view.Padding.ViewToScreen (-3, -2, out col, out row);
+			Assert.Equal (1, col);
+			Assert.Equal (1, row);
+			view.ViewToScreen (-3, -2, out col, out row);
+			Assert.Equal (1, col);
+			Assert.Equal (1, row);
+			view.ViewToScreen (-4, -3, out col, out row);
+			Assert.Equal (0, col);
+			Assert.Equal (0, row);
+			Assert.Equal (top, View.FindDeepestView (top, 0, 0, out rx, out ry));
+			Assert.Equal (0, rx);
+			Assert.Equal (0, ry);
+			Assert.Equal (new Point (-1, -1), view.ScreenToView (3, 2));
+			view.ViewToScreen (0, 0, out col, out row);
+			Assert.Equal (4, col);
+			Assert.Equal (3, row);
+			Assert.Equal (view, View.FindDeepestView (top, 4, 3, out rx, out ry));
+			Assert.Equal (0, rx);
+			Assert.Equal (0, ry);
+			Assert.Equal (new Point (9, -1), view.ScreenToView (13, 2));
+			view.ViewToScreen (10, 0, out col, out row);
+			Assert.Equal (14, col);
+			Assert.Equal (3, row);
+			Assert.Equal (top, View.FindDeepestView (top, 14, 3, out rx, out ry));
+			Assert.Equal (14, rx);
+			Assert.Equal (3, ry);
+			Assert.Equal (new Point (10, 0), view.ScreenToView (14, 3));
+			view.ViewToScreen (11, 1, out col, out row);
+			Assert.Equal (15, col);
+			Assert.Equal (4, row);
+			Assert.Equal (top, View.FindDeepestView (top, 15, 4, out rx, out ry));
+			Assert.Equal (15, rx);
+			Assert.Equal (4, ry);
+		}
+
+		[Fact, AutoInitShutdown]
+		public void ScreenToView_ViewToScreen_FindDeepestView_Smaller_Top ()
+		{
+			var top = new Toplevel () { X = 3, Y = 2, Width = 20, Height = 10, BorderStyle = LineStyle.Single };
+			var view = new View () { X = 3, Y = 2, Width = 10, Height = 1, Text = "0123456789" };
+			top.Add (view);
+
+			Application.Begin (top);
+
+			Assert.Equal (Application.Current, top);
+			Assert.Equal (new Rect (0, 0, 80, 25), new Rect (0, 0, View.Driver.Cols, View.Driver.Rows));
+			Assert.NotEqual (new Rect (0, 0, View.Driver.Cols, View.Driver.Rows), top.Frame);
+			Assert.Equal (new Rect (3, 2, 20, 10), top.Frame);
+
+			((FakeDriver)Application.Driver).SetBufferSize (30, 20);
+			Assert.Equal (new Rect (0, 0, 30, 20), new Rect (0, 0, View.Driver.Cols, View.Driver.Rows));
+			Assert.NotEqual (new Rect (0, 0, View.Driver.Cols, View.Driver.Rows), top.Frame);
+			Assert.Equal (new Rect (3, 2, 20, 10), top.Frame);
+			var frame = TestHelpers.AssertDriverContentsWithFrameAre (@"
+   ┌──────────────────┐
+   │                  │
+   │                  │
+   │   0123456789     │
+   │                  │
+   │                  │
+   │                  │
+   │                  │
+   │                  │
+   └──────────────────┘", output);
+			// mean the output started at col 3 and line 2
+			// which result with a width of 23 and a height of 10 on the output
+			Assert.Equal (new Rect (3, 2, 23, 10), frame);
+
+			// top
+			Assert.Equal (new Point (-3, -2), top.ScreenToView (0, 0));
+			top.Margin.ViewToScreen (-3, -2, out int col, out int row);
+			Assert.Equal (0, col);
+			Assert.Equal (0, row);
+			top.Border.ViewToScreen (-3, -2, out col, out row);
+			Assert.Equal (0, col);
+			Assert.Equal (0, row);
+			top.Padding.ViewToScreen (-3, -2, out col, out row);
+			Assert.Equal (0, col);
+			Assert.Equal (0, row);
+			top.ViewToScreen (-3, -2, out col, out row);
+			Assert.Equal (1, col);
+			Assert.Equal (1, row);
+			top.ViewToScreen (-4, -3, out col, out row);
+			Assert.Equal (0, col);
+			Assert.Equal (0, row);
+			Assert.Null (View.FindDeepestView (top, -4, -3, out int rx, out int ry));
+			Assert.Equal (0, rx);
+			Assert.Equal (0, ry);
+			Assert.Equal (Point.Empty, top.ScreenToView (3, 2));
+			top.ViewToScreen (0, 0, out col, out row);
+			Assert.Equal (4, col);
+			Assert.Equal (3, row);
+			Assert.Equal (top, View.FindDeepestView (top, 3, 2, out rx, out ry));
+			Assert.Equal (0, rx);
+			Assert.Equal (0, ry);
+			Assert.Equal (new Point (10, 0), top.ScreenToView (13, 2));
+			top.ViewToScreen (10, 0, out col, out row);
+			Assert.Equal (14, col);
+			Assert.Equal (3, row);
+			Assert.Equal (top, View.FindDeepestView (top, 13, 2, out rx, out ry));
+			Assert.Equal (10, rx);
+			Assert.Equal (0, ry);
+			Assert.Equal (new Point (11, 1), top.ScreenToView (14, 3));
+			top.ViewToScreen (11, 1, out col, out row);
+			Assert.Equal (15, col);
+			Assert.Equal (4, row);
+			Assert.Equal (top, View.FindDeepestView (top, 14, 3, out rx, out ry));
+			Assert.Equal (11, rx);
+			Assert.Equal (1, ry);
+			// view
+			Assert.Equal (new Point (-7, -5), view.ScreenToView (0, 0));
+			view.Margin.ViewToScreen (-6, -4, out col, out row);
+			Assert.Equal (1, col);
+			Assert.Equal (1, row);
+			view.Border.ViewToScreen (-6, -4, out col, out row);
+			Assert.Equal (1, col);
+			Assert.Equal (1, row);
+			view.Padding.ViewToScreen (-6, -4, out col, out row);
+			Assert.Equal (1, col);
+			Assert.Equal (1, row);
+			view.ViewToScreen (-6, -4, out col, out row);
+			Assert.Equal (1, col);
+			Assert.Equal (1, row);
+			Assert.Null (View.FindDeepestView (top, 1, 1, out rx, out ry));
+			Assert.Equal (0, rx);
+			Assert.Equal (0, ry);
+			Assert.Equal (new Point (-4, -3), view.ScreenToView (3, 2));
+			view.ViewToScreen (-3, -2, out col, out row);
+			Assert.Equal (4, col);
+			Assert.Equal (3, row);
+			Assert.Equal (top, View.FindDeepestView (top, 4, 3, out rx, out ry));
+			Assert.Equal (1, rx);
+			Assert.Equal (1, ry);
+			Assert.Equal (new Point (-1, -1), view.ScreenToView (6, 4));
+			view.ViewToScreen (0, 0, out col, out row);
+			Assert.Equal (7, col);
+			Assert.Equal (5, row);
+			Assert.Equal (view, View.FindDeepestView (top, 7, 5, out rx, out ry));
+			Assert.Equal (0, rx);
+			Assert.Equal (0, ry);
+			Assert.Equal (new Point (6, -1), view.ScreenToView (13, 4));
+			view.ViewToScreen (7, 0, out col, out row);
+			Assert.Equal (14, col);
+			Assert.Equal (5, row);
+			Assert.Equal (view, View.FindDeepestView (top, 14, 5, out rx, out ry));
+			Assert.Equal (7, rx);
+			Assert.Equal (0, ry);
+			Assert.Equal (new Point (7, -2), view.ScreenToView (14, 3));
+			view.ViewToScreen (8, -1, out col, out row);
+			Assert.Equal (15, col);
+			Assert.Equal (4, row);
+			Assert.Equal (top, View.FindDeepestView (top, 15, 4, out rx, out ry));
+			Assert.Equal (12, rx);
+			Assert.Equal (2, ry);
+			Assert.Equal (new Point (16, -2), view.ScreenToView (23, 3));
+			view.ViewToScreen (17, -1, out col, out row);
+			Assert.Equal (24, col);
+			Assert.Equal (4, row);
+			Assert.Null (View.FindDeepestView (top, 24, 4, out rx, out ry));
+			Assert.Equal (0, rx);
+			Assert.Equal (0, ry);
+		}
 	}
 }

+ 165 - 8
UnitTests/Views/ContextMenuTests.cs

@@ -380,6 +380,8 @@ namespace Terminal.Gui.ViewsTests {
 		[Fact, AutoInitShutdown]
 		public void Show_Display_At_Zero_If_The_Toplevel_Width_Is_Less_Than_The_Menu_Width ()
 		{
+			((FakeDriver)Application.Driver).SetBufferSize (5, 25);
+
 			var cm = new ContextMenu (0, 0,
 				new MenuBarItem (new MenuItem [] {
 					new MenuItem ("One", "", null),
@@ -392,14 +394,12 @@ namespace Terminal.Gui.ViewsTests {
 			cm.Show ();
 			Assert.Equal (new Point (0, 0), cm.Position);
 			Application.Begin (Application.Top);
-			((FakeDriver)Application.Driver).SetBufferSize (5, 25);
 
 			var expected = @"
 ┌────
 │ One
 │ Two
-└────
-";
+└────";
 
 			var pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
 			Assert.Equal (new Rect (0, 1, 5, 4), pos);
@@ -411,6 +411,8 @@ namespace Terminal.Gui.ViewsTests {
 		[Fact, AutoInitShutdown]
 		public void Show_Display_At_Zero_If_The_Toplevel_Height_Is_Less_Than_The_Menu_Height ()
 		{
+			((FakeDriver)Application.Driver).SetBufferSize (80, 3);
+
 			var cm = new ContextMenu (0, 0,
 				new MenuBarItem (new MenuItem [] {
 					new MenuItem ("One", "", null),
@@ -423,13 +425,11 @@ namespace Terminal.Gui.ViewsTests {
 			cm.Show ();
 			Assert.Equal (new Point (0, 0), cm.Position);
 			Application.Begin (Application.Top);
-			((FakeDriver)Application.Driver).SetBufferSize (80, 3);
 
 			var expected = @"
 ┌──────┐
 │ One  │
-│ Two  │
-";
+│ Two  │";
 
 			var pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
 			Assert.Equal (new Rect (0, 0, 8, 3), pos);
@@ -903,9 +903,8 @@ namespace Terminal.Gui.ViewsTests {
 			Assert.Null (tf.ContextMenu.MenuBar);
 		}
 
-		// BUGBUG: Broke this test with #2483 - @bdisp I need your help figuring out why
 		[Fact, AutoInitShutdown]
-		public void Draw_A_ContextManu_Over_A_Dialog ()
+		public void Draw_A_ContextMenu_Over_A_Dialog ()
 		{
 			var top = Application.Top;
 			var win = new Window ();
@@ -983,5 +982,163 @@ namespace Terminal.Gui.ViewsTests {
 
 			Application.End (rs);
 		}
+
+		[Fact, AutoInitShutdown]
+		public void Draw_A_ContextMenu_Over_A_Top_Dialog ()
+		{
+			((FakeDriver)Application.Driver).SetBufferSize (20, 15);
+
+			Assert.Equal (new Rect (0, 0, 20, 15), Application.Driver.Clip);
+			TestHelpers.AssertDriverContentsWithFrameAre ("", output);
+
+			var dialog = new Dialog () { X = 2, Y = 2, Width = 15, Height = 4 };
+			dialog.Add (new TextField ("Test") { X = Pos.Center (), Width = 10 });
+			var rs = Application.Begin (dialog);
+
+			Assert.Equal (new Rect (2, 2, 15, 4), dialog.Frame);
+			Assert.Equal (dialog, Application.Top);
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+  ┌─────────────┐
+  │ Test        │
+  │             │
+  └─────────────┘", output);
+
+			ReflectionTools.InvokePrivate (
+				typeof (Application),
+				"ProcessMouseEvent",
+				new MouseEvent () {
+					X = 9,
+					Y = 3,
+					Flags = MouseFlags.Button3Clicked
+				});
+
+			var firstIteration = false;
+			Application.RunMainLoopIteration (ref rs, true, ref firstIteration);
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+  ┌─────────────┐   
+  │ Test        │   
+┌───────────────────
+│ Select All   Ctrl+
+│ Delete All   Ctrl+
+│ Copy         Ctrl+
+│ Cut          Ctrl+
+│ Paste        Ctrl+
+│ Undo         Ctrl+
+│ Redo         Ctrl+
+└───────────────────", output);
+
+			Application.End (rs);
+		}
+
+		[Fact, AutoInitShutdown]
+		public void Draw_A_ContextMenu_Over_A_Borderless_Top ()
+		{
+			((FakeDriver)Application.Driver).SetBufferSize (20, 15);
+
+			Assert.Equal (new Rect (0, 0, 20, 15), Application.Driver.Clip);
+			TestHelpers.AssertDriverContentsWithFrameAre ("", output);
+
+			var top = new Toplevel () { X = 2, Y = 2, Width = 15, Height = 4 };
+			top.Add (new TextField ("Test") { X = Pos.Center (), Width = 10 });
+			var rs = Application.Begin (top);
+
+			Assert.Equal (new Rect (2, 2, 15, 4), top.Frame);
+			Assert.Equal (top, Application.Top);
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+    Test", output);
+
+			ReflectionTools.InvokePrivate (
+				typeof (Application),
+				"ProcessMouseEvent",
+				new MouseEvent () {
+					X = 8,
+					Y = 2,
+					Flags = MouseFlags.Button3Clicked
+				});
+
+			var firstIteration = false;
+			Application.RunMainLoopIteration (ref rs, true, ref firstIteration);
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+    Test            
+┌───────────────────
+│ Select All   Ctrl+
+│ Delete All   Ctrl+
+│ Copy         Ctrl+
+│ Cut          Ctrl+
+│ Paste        Ctrl+
+│ Undo         Ctrl+
+│ Redo         Ctrl+
+└───────────────────", output);
+
+			Application.End (rs);
+		}
+
+		[Fact, AutoInitShutdown]
+		public void UseSubMenusSingleFrame_True_By_Mouse ()
+		{
+			var cm = new ContextMenu (5, 10,
+				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),
+				})
+			) { UseSubMenusSingleFrame = true };
+
+			cm.Show ();
+			var rs = Application.Begin (Application.Top);
+
+			Assert.Equal (new Rect (5, 11, 10, 5), Application.Top.Subviews [0].Frame);
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+     ┌────────┐
+     │ One    │
+     │ Two   ►│
+     │ Three  │
+     └────────┘", output);
+
+			ReflectionTools.InvokePrivate (
+				typeof (Application),
+				"ProcessMouseEvent",
+				new MouseEvent () {
+					X = 5,
+					Y = 13,
+					Flags = MouseFlags.Button1Clicked
+				});
+
+			var firstIteration = false;
+			Application.RunMainLoopIteration (ref rs, true, ref firstIteration);
+			Assert.Equal (new Rect (5, 11, 10, 5), Application.Top.Subviews [0].Frame);
+			Assert.Equal (new Rect (5, 11, 15, 6), Application.Top.Subviews [1].Frame);
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+     ┌─────────────┐
+     │◄    Two     │
+     ├─────────────┤
+     │ Sub-Menu 1  │
+     │ Sub-Menu 2  │
+     └─────────────┘", output);
+
+			ReflectionTools.InvokePrivate (
+				typeof (Application),
+				"ProcessMouseEvent",
+				new MouseEvent () {
+					X = 5,
+					Y = 12,
+					Flags = MouseFlags.Button1Clicked
+				});
+
+			firstIteration = false;
+			Application.RunMainLoopIteration (ref rs, true, ref firstIteration);
+			Assert.Equal (new Rect (5, 11, 10, 5), Application.Top.Subviews [0].Frame);
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+     ┌────────┐
+     │ One    │
+     │ Two   ►│
+     │ Three  │
+     └────────┘", output);
+
+			Application.End (rs);
+		}
 	}
 }

文件差异内容过多而无法显示
+ 526 - 272
UnitTests/Views/MenuTests.cs


部分文件因为文件数量过多而无法显示