From a3059ae4a565bd8cad405c764e0aec77d8a2c5e7 Mon Sep 17 00:00:00 2001 From: BDisp Date: Mon, 14 Nov 2022 18:26:38 +0000 Subject: [PATCH 1/4] Fixes System.NullReferenceException exception. --- Terminal.Gui/Windows/FileDialog.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Terminal.Gui/Windows/FileDialog.cs b/Terminal.Gui/Windows/FileDialog.cs index 53e37558e..26b07688b 100644 --- a/Terminal.Gui/Windows/FileDialog.cs +++ b/Terminal.Gui/Windows/FileDialog.cs @@ -116,11 +116,17 @@ namespace Terminal.Gui { void Watcher_Error (object sender, ErrorEventArgs e) { + if (Application.MainLoop == null) + return; + Application.MainLoop.Invoke (() => Reload ()); } void Watcher_Changed (object sender, FileSystemEventArgs e) { + if (Application.MainLoop == null) + return; + Application.MainLoop.Invoke (() => Reload ()); } From 68c93ed540244c79e6b16a5e3a505c643970b0d3 Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 16 Nov 2022 19:37:04 +0000 Subject: [PATCH 2/4] Fixes #2225. MenuBar leaves trails when mounted in a Window. --- Terminal.Gui/Core/Window.cs | 5 ++- Terminal.Gui/Views/Menu.cs | 18 +++++++- UnitTests/MenuTests.cs | 82 ++++++++++++++++++++++++++++++++++++- 3 files changed, 101 insertions(+), 4 deletions(-) diff --git a/Terminal.Gui/Core/Window.cs b/Terminal.Gui/Core/Window.cs index d0811eb16..42d646ddb 100644 --- a/Terminal.Gui/Core/Window.cs +++ b/Terminal.Gui/Core/Window.cs @@ -280,15 +280,16 @@ namespace Terminal.Gui { //var borderLength = Border.DrawMarginFrame ? 1 : 0; // FIXED: Why do we draw the frame twice? This call is here to clear the content area, I think. Why not just clear that area? - if (!NeedDisplay.IsEmpty) { + if (!NeedDisplay.IsEmpty || ChildNeedsDisplay || LayoutNeeded) { Driver.SetAttribute (GetNormalColor ()); Clear (); + contentView.SetNeedsDisplay (); } var savedClip = contentView.ClipToBounds (); // Redraw our contentView // DONE: smartly constrict contentView.Bounds to just be what intersects with the 'bounds' we were passed - contentView.Redraw (!NeedDisplay.IsEmpty ? contentView.Bounds : bounds); + contentView.Redraw (!NeedDisplay.IsEmpty || ChildNeedsDisplay || LayoutNeeded ? contentView.Bounds : bounds); Driver.Clip = savedClip; ClearLayoutNeeded (); diff --git a/Terminal.Gui/Views/Menu.cs b/Terminal.Gui/Views/Menu.cs index 3df94d23a..3010eb8aa 100644 --- a/Terminal.Gui/Views/Menu.cs +++ b/Terminal.Gui/Views/Menu.cs @@ -690,6 +690,7 @@ namespace Terminal.Gui { } } while (barItems.Children [current] == null || disabled); SetNeedsDisplay (); + SetParentSetNeedsDisplay (); if (!host.UseSubMenusSingleFrame) host.OnMenuOpened (); return true; @@ -737,11 +738,24 @@ namespace Terminal.Gui { } } while (barItems.Children [current] == null || disabled); SetNeedsDisplay (); + SetParentSetNeedsDisplay (); if (!host.UseSubMenusSingleFrame) host.OnMenuOpened (); return true; } + private void SetParentSetNeedsDisplay () + { + if (host.openSubMenu != null) { + foreach (var menu in host.openSubMenu) { + menu.SetNeedsDisplay (); + } + } + + host?.openMenu.SetNeedsDisplay (); + host.SetNeedsDisplay (); + } + public override bool MouseEvent (MouseEvent me) { if (!host.handled && !host.HandleGrabView (me, this)) { @@ -778,6 +792,7 @@ namespace Terminal.Gui { current = me.Y - 1; if (host.UseSubMenusSingleFrame || !CheckSubMenu ()) { SetNeedsDisplay (); + SetParentSetNeedsDisplay (); return true; } host.OnMenuOpened (); @@ -806,6 +821,7 @@ namespace Terminal.Gui { return host.CloseMenu (false, true); } else { SetNeedsDisplay (); + SetParentSetNeedsDisplay (); } return true; } @@ -1587,7 +1603,7 @@ namespace Terminal.Gui { var subMenu = openCurrentMenu.current > -1 && openCurrentMenu.barItems.Children.Length > 0 ? openCurrentMenu.barItems.SubMenu (openCurrentMenu.barItems.Children [openCurrentMenu.current]) : null; - if ((selectedSub == -1 || openSubMenu == null || openSubMenu?.Count == selectedSub) && subMenu == null) { + if ((selectedSub == -1 || openSubMenu == null || openSubMenu?.Count - 1 == selectedSub) && subMenu == null) { if (openSubMenu != null && !CloseMenu (false, true)) return; NextMenu (false, ignoreUseSubMenusSingleFrame); diff --git a/UnitTests/MenuTests.cs b/UnitTests/MenuTests.cs index 613b3afb5..dc23736b5 100644 --- a/UnitTests/MenuTests.cs +++ b/UnitTests/MenuTests.cs @@ -1498,7 +1498,7 @@ Edit Assert.True (menu.IsMenuOpen); Assert.False (tf.HasFocus); Application.Top.Redraw (Application.Top.Bounds); - TestHelpers.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen(0), output); + TestHelpers.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 ()))); @@ -1636,5 +1636,85 @@ Edit 00000000000000 00000000000000", attributes); } + + [Fact, AutoInitShutdown] + public void MenuBar_In_Window_Without_Other_Views () + { + var win = new Window (); + var menu = new MenuBar (new MenuBarItem [] { + new MenuBarItem ("File", new MenuItem [] { + new MenuItem ("New", "", null) + }), + new MenuBarItem ("Edit", new MenuItem [] { + new MenuBarItem ("Delete", new MenuItem [] { + new MenuItem ("All", "", null), + new MenuItem ("Selected", "", null) + }) + }) + }); ; + win.Add (menu); + var top = Application.Top; + top.Add (win); + Application.Begin (top); + ((FakeDriver)Application.Driver).SetBufferSize (40, 8); + + TestHelpers.AssertDriverContentsWithFrameAre (@" +┌──────────────────────────────────────┐ +│ File Edit │ +│ │ +│ │ +│ │ +│ │ +│ │ +└──────────────────────────────────────┘", output); + + Assert.True (win.ProcessHotKey (new KeyEvent (Key.F9, new KeyModifiers ()))); + win.Redraw (win.Bounds); + TestHelpers.AssertDriverContentsWithFrameAre (@" +┌──────────────────────────────────────┐ +│ File Edit │ +│┌──────┐ │ +││ New │ │ +│└──────┘ │ +│ │ +│ │ +└──────────────────────────────────────┘", output); + + Assert.True (menu.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ()))); + win.Redraw (win.Bounds); + TestHelpers.AssertDriverContentsWithFrameAre (@" +┌──────────────────────────────────────┐ +│ File Edit │ +│ ┌─────────┐ │ +│ │ Delete ►│ │ +│ └─────────┘ │ +│ │ +│ │ +└──────────────────────────────────────┘", output); + + Assert.True (menu.openMenu.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ()))); + win.Redraw (win.Bounds); + TestHelpers.AssertDriverContentsWithFrameAre (@" +┌──────────────────────────────────────┐ +│ File Edit │ +│ ┌─────────┐ │ +│ │ Delete ►│┌───────────┐ │ +│ └─────────┘│ All │ │ +│ │ Selected │ │ +│ └───────────┘ │ +└──────────────────────────────────────┘", output); + + Assert.True (menu.openMenu.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ()))); + win.Redraw (win.Bounds); + TestHelpers.AssertDriverContentsWithFrameAre (@" +┌──────────────────────────────────────┐ +│ File Edit │ +│┌──────┐ │ +││ New │ │ +│└──────┘ │ +│ │ +│ │ +└──────────────────────────────────────┘", output); + } } } From 6986ab255e5f7cd54e2f47cd7c3dfb3d2846bc78 Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 16 Nov 2022 19:38:57 +0000 Subject: [PATCH 3/4] Avoids SetNeedsDisplay on views already removed. --- Terminal.Gui/Core/View.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Terminal.Gui/Core/View.cs b/Terminal.Gui/Core/View.cs index 274f40de9..0c39eab41 100644 --- a/Terminal.Gui/Core/View.cs +++ b/Terminal.Gui/Core/View.cs @@ -987,13 +987,13 @@ namespace Terminal.Gui { if (view == null || subviews == null) return; - SetNeedsLayout (); - SetNeedsDisplay (); var touched = view.Frame; subviews.Remove (view); tabIndexes.Remove (view); view.container = null; view.tabIndex = -1; + SetNeedsLayout (); + SetNeedsDisplay (); if (subviews.Count < 1) { CanFocus = false; } From 2ae20f7de7ae934cb2a7fcf88a891f032e146da4 Mon Sep 17 00:00:00 2001 From: BDisp Date: Sun, 4 Dec 2022 20:11:00 +0000 Subject: [PATCH 4/4] Removed comments as requested. --- Terminal.Gui/Core/Window.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Terminal.Gui/Core/Window.cs b/Terminal.Gui/Core/Window.cs index 42d646ddb..85c8929cc 100644 --- a/Terminal.Gui/Core/Window.cs +++ b/Terminal.Gui/Core/Window.cs @@ -277,9 +277,7 @@ namespace Terminal.Gui { { var padding = Border.GetSumThickness (); var scrRect = ViewToScreen (new Rect (0, 0, Frame.Width, Frame.Height)); - //var borderLength = Border.DrawMarginFrame ? 1 : 0; - // FIXED: Why do we draw the frame twice? This call is here to clear the content area, I think. Why not just clear that area? if (!NeedDisplay.IsEmpty || ChildNeedsDisplay || LayoutNeeded) { Driver.SetAttribute (GetNormalColor ()); Clear ();