From f2e5b08a88a23d2c431a47ec9f0b893b0f2759a2 Mon Sep 17 00:00:00 2001 From: Fabian R Date: Sat, 23 May 2020 18:41:41 -0500 Subject: [PATCH] + Added IsCurrentTop read-only property to Gui.Toplevel class to allow a more convenient checking if a view's instance is currently on top (active and displayed). - Fixed an elusive crash that may occur in the Application.RunLoop method due to a null'ed Toplevel.NeedDisplay property. This issue appears to be caused by a race condition that may occur when switching Views (TopLevel) too fast. Extended to all other NeedDisplay checks too. - ListView control now displays empty rows for Null items in its Items collection, instead of crashing with a NullReferenceException. - Improved MenuBarItem constructor behaviour by performing an additional sanity check on the MenuItem[] children parameter to ensure its not null. If so, raise an ArgumentNullException. Using an empty array of type MenuItem for this parameter still displays an empty menu as intended. --- Terminal.Gui/Core.cs | 21 +++++++++++++++------ Terminal.Gui/Views/ListView.cs | 16 ++++++++++------ Terminal.Gui/Views/Menu.cs | 3 +++ 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/Terminal.Gui/Core.cs b/Terminal.Gui/Core.cs index fa0b41354..397f9f462 100644 --- a/Terminal.Gui/Core.cs +++ b/Terminal.Gui/Core.cs @@ -350,6 +350,15 @@ namespace Terminal.Gui { /// The identifier. public ustring Id { get; set; } = ""; + /// + /// Returns a value indicating if this View is currently on Top (Active) + /// + public bool IsCurrentTop { + get { + return Application.Current == this; + } + } + /// /// Gets or sets a value indicating whether this want mouse position reports. /// @@ -531,7 +540,7 @@ namespace Terminal.Gui { /// The region that must be flagged for repaint. public void SetNeedsDisplay (Rect region) { - if (NeedDisplay.IsEmpty) + if (NeedDisplay == null || NeedDisplay.IsEmpty) NeedDisplay = region; else { var x = Math.Min (NeedDisplay.X, region.X); @@ -1015,7 +1024,7 @@ namespace Terminal.Gui { if (subviews != null) { foreach (var view in subviews) { - if (!view.NeedDisplay.IsEmpty || view.childNeedsDisplay) { + if (view.NeedDisplay != null && (!view.NeedDisplay.IsEmpty || view.childNeedsDisplay)) { if (view.Frame.IntersectsWith (clipRect) && view.Frame.IntersectsWith (region)) { // FIXED: optimize this by computing the intersection of region and view.Bounds @@ -1704,8 +1713,8 @@ namespace Terminal.Gui { { Application.CurrentView = this; - if (this == Application.Top || this == Application.Current) { - if (!NeedDisplay.IsEmpty) { + if (IsCurrentTop) { + if (NeedDisplay != null && !NeedDisplay.IsEmpty) { Driver.SetAttribute (Colors.TopLevel.Normal); Clear (region); Driver.SetAttribute (Colors.Base.Normal); @@ -1887,7 +1896,7 @@ namespace Terminal.Gui { { Application.CurrentView = this; - if (!NeedDisplay.IsEmpty) { + if (NeedDisplay != null && !NeedDisplay.IsEmpty) { DrawFrameWindow (); } contentView.Redraw (contentView.Bounds); @@ -2487,7 +2496,7 @@ namespace Terminal.Gui { Iteration?.Invoke (null, EventArgs.Empty); } else if (wait == false) return; - if (!state.Toplevel.NeedDisplay.IsEmpty || state.Toplevel.childNeedsDisplay) { + if (state.Toplevel.NeedDisplay != null && (!state.Toplevel.NeedDisplay.IsEmpty || state.Toplevel.childNeedsDisplay)) { state.Toplevel.Redraw (state.Toplevel.Bounds); if (DebugDrawBounds) DrawBounds (state.Toplevel); diff --git a/Terminal.Gui/Views/ListView.cs b/Terminal.Gui/Views/ListView.cs index e3c53557b..c325485cb 100644 --- a/Terminal.Gui/Views/ListView.cs +++ b/Terminal.Gui/Views/ListView.cs @@ -593,12 +593,16 @@ namespace Terminal.Gui { { container.Move (col, line); var t = src [item]; - if (t is ustring) { - RenderUstr (driver, (ustring)t, col, line, width); - } else if (t is string) { - RenderUstr (driver, (string)t, col, line, width); - } else - RenderUstr (driver, t.ToString (), col, line, width); + if (t == null) { + RenderUstr (driver, ustring.Make(""), col, line, width); + } else { + if (t is ustring) { + RenderUstr (driver, (ustring)t, col, line, width); + } else if (t is string) { + RenderUstr (driver, (string)t, col, line, width); + } else + RenderUstr (driver, t.ToString (), col, line, width); + } } /// diff --git a/Terminal.Gui/Views/Menu.cs b/Terminal.Gui/Views/Menu.cs index 0a920a8ed..688bea039 100644 --- a/Terminal.Gui/Views/Menu.cs +++ b/Terminal.Gui/Views/Menu.cs @@ -162,6 +162,9 @@ namespace Terminal.Gui { /// The items in the current menu. public MenuBarItem (ustring title, MenuItem [] children) { + if (children == null) + throw new ArgumentNullException (nameof (children), "The parameter cannot be null. Use an empty array instead."); + SetTitle (title ?? ""); Children = children; }