瀏覽代碼

Merge pull request #3339 from tig/v2_3338-Toplevel-Must-Be-Disposed

Fixes #3338. `Application.Run/End` -> Callers must dispose Toplevel
Tig 1 年之前
父節點
當前提交
6070e80e23
共有 100 個文件被更改,包括 1642 次插入1002 次删除
  1. 4 2
      Example/Example.cs
  2. 1 1
      Terminal.Gui/Application.MainLoopSyncContext.cs
  3. 228 168
      Terminal.Gui/Application.cs
  4. 2 3
      Terminal.Gui/Configuration/ConfigurationManager.cs
  5. 9 2
      Terminal.Gui/Configuration/SettingsScope.cs
  6. 1 0
      Terminal.Gui/FileServices/DefaultFileOperations.cs
  7. 16 1
      Terminal.Gui/View/View.cs
  8. 37 1
      Terminal.Gui/Views/Dialog.cs
  9. 9 6
      Terminal.Gui/Views/FileDialog.cs
  10. 9 4
      Terminal.Gui/Views/ListView.cs
  11. 3 0
      Terminal.Gui/Views/Menu/ContextMenu.cs
  12. 1 0
      Terminal.Gui/Views/MessageBox.cs
  13. 1 1
      Terminal.Gui/Views/OpenDialog.cs
  14. 1 1
      Terminal.Gui/Views/SaveDialog.cs
  15. 1 1
      Terminal.Gui/Views/TableView/TableView.cs
  16. 4 4
      Terminal.Gui/Views/Toplevel.cs
  17. 1 1
      Terminal.Gui/Views/Wizard/Wizard.cs
  18. 1 0
      UICatalog/KeyBindingsDialog.cs
  19. 80 55
      UICatalog/Scenario.cs
  20. 5 4
      UICatalog/Scenarios/ASCIICustomButton.cs
  21. 7 8
      UICatalog/Scenarios/AdornmentExperiments.cs
  22. 5 2
      UICatalog/Scenarios/Adornments.cs
  23. 6 8
      UICatalog/Scenarios/AllViewsTester.cs
  24. 18 5
      UICatalog/Scenarios/Animation.cs
  25. 82 41
      UICatalog/Scenarios/BackgroundWorkerCollection.cs
  26. 1 1
      UICatalog/Scenarios/Buttons.cs
  27. 10 7
      UICatalog/Scenarios/CharacterMap.cs
  28. 21 21
      UICatalog/Scenarios/ChineseUI.cs
  29. 1 1
      UICatalog/Scenarios/ClassExplorer.cs
  30. 4 3
      UICatalog/Scenarios/Clipping.cs
  31. 8 7
      UICatalog/Scenarios/CollectionNavigatorTester.cs
  32. 3 2
      UICatalog/Scenarios/CombiningMarks.cs
  33. 36 35
      UICatalog/Scenarios/ComputedLayout.cs
  34. 17 19
      UICatalog/Scenarios/ConfigurationEditor.cs
  35. 1 1
      UICatalog/Scenarios/ContextMenus.cs
  36. 4 2
      UICatalog/Scenarios/CsvEditor.cs
  37. 2 1
      UICatalog/Scenarios/Dialogs.cs
  38. 13 10
      UICatalog/Scenarios/DynamicMenuBar.cs
  39. 13 10
      UICatalog/Scenarios/DynamicStatusBar.cs
  40. 16 9
      UICatalog/Scenarios/Editor.cs
  41. 10 3
      UICatalog/Scenarios/FileDialogExamples.cs
  42. 17 25
      UICatalog/Scenarios/Generic.cs
  43. 2 2
      UICatalog/Scenarios/GraphViewExample.cs
  44. 3 2
      UICatalog/Scenarios/HexEditor.cs
  45. 24 23
      UICatalog/Scenarios/HotKeys.cs
  46. 3 0
      UICatalog/Scenarios/Images.cs
  47. 3 2
      UICatalog/Scenarios/InteractiveTree.cs
  48. 7 3
      UICatalog/Scenarios/LineCanvasExperiment.cs
  49. 2 2
      UICatalog/Scenarios/LineViewExample.cs
  50. 3 2
      UICatalog/Scenarios/ListColumns.cs
  51. 3 1
      UICatalog/Scenarios/Localization.cs
  52. 17 16
      UICatalog/Scenarios/MenuBarScenario.cs
  53. 2 2
      UICatalog/Scenarios/MessageBoxes.cs
  54. 3 2
      UICatalog/Scenarios/MultiColouredTable.cs
  55. 120 108
      UICatalog/Scenarios/Notepad.cs
  56. 7 3
      UICatalog/Scenarios/ProgressBarStyles.cs
  57. 6 3
      UICatalog/Scenarios/RunTExample.cs
  58. 4 2
      UICatalog/Scenarios/RuneWidthGreaterThanOne.cs
  59. 4 4
      UICatalog/Scenarios/Scrolling.cs
  60. 33 11
      UICatalog/Scenarios/SingleBackgroundWorker.cs
  61. 1 1
      UICatalog/Scenarios/Sliders.cs
  62. 2 2
      UICatalog/Scenarios/SpinnerStyles.cs
  63. 2 2
      UICatalog/Scenarios/SyntaxHighlighting.cs
  64. 2 2
      UICatalog/Scenarios/TabViewExample.cs
  65. 4 2
      UICatalog/Scenarios/TableEditor.cs
  66. 1 1
      UICatalog/Scenarios/TextFormatterDemo.cs
  67. 2 2
      UICatalog/Scenarios/TextViewAutocompletePopup.cs
  68. 2 2
      UICatalog/Scenarios/Threading.cs
  69. 1 1
      UICatalog/Scenarios/TileViewNesting.cs
  70. 2 2
      UICatalog/Scenarios/TreeUseCases.cs
  71. 1 1
      UICatalog/Scenarios/TreeViewFileSystem.cs
  72. 2 2
      UICatalog/Scenarios/Unicode.cs
  73. 8 7
      UICatalog/Scenarios/ViewExperiments.cs
  74. 5 5
      UICatalog/Scenarios/WindowsAndFrameViews.cs
  75. 4 3
      UICatalog/Scenarios/WizardAsView.cs
  76. 3 2
      UICatalog/Scenarios/Wizards.cs
  77. 7 11
      UICatalog/UICatalog.cs
  78. 207 32
      UnitTests/Application/ApplicationTests.cs
  79. 29 24
      UnitTests/Application/KeyboardTests.cs
  80. 6 4
      UnitTests/Application/MainLoopTests.cs
  81. 15 12
      UnitTests/Application/MouseTests.cs
  82. 1 0
      UnitTests/Application/RunStateTests.cs
  83. 2 2
      UnitTests/Application/SynchronizatonContextTests.cs
  84. 2 2
      UnitTests/Configuration/ConfigurationMangerTests.cs
  85. 6 4
      UnitTests/ConsoleDrivers/ConsoleDriverTests.cs
  86. 206 61
      UnitTests/Dialogs/DialogTests.cs
  87. 24 39
      UnitTests/Dialogs/MessageBoxTests.cs
  88. 3 2
      UnitTests/Drawing/LineCanvasTests.cs
  89. 12 8
      UnitTests/Drawing/RulerTests.cs
  90. 3 2
      UnitTests/Drawing/ThicknessTests.cs
  91. 4 3
      UnitTests/Input/EscSeqUtilsTests.cs
  92. 3 1
      UnitTests/TestHelpers.cs
  93. 2 2
      UnitTests/Text/AutocompleteTests.cs
  94. 29 33
      UnitTests/UICatalog/ScenarioTests.cs
  95. 8 6
      UnitTests/View/Adornment/BorderTests.cs
  96. 13 9
      UnitTests/View/DrawTests.cs
  97. 9 8
      UnitTests/View/Layout/DimTests.cs
  98. 24 15
      UnitTests/View/Layout/LayoutTests.cs
  99. 27 20
      UnitTests/View/Layout/PosTests.cs
  100. 3 1
      UnitTests/View/MouseTests.cs

+ 4 - 2
Example/Example.cs

@@ -6,9 +6,11 @@
 using System;
 using Terminal.Gui;
 
-Application.Run<ExampleWindow> ();
+var app = Application.Run<ExampleWindow> ();
 
-Console.WriteLine ($"Username: {((ExampleWindow)Application.Top).UserNameText.Text}");
+Console.WriteLine ($"Username: {app.UserNameText.Text}");
+
+app.Dispose ();
 
 // Before the application exits, reset Terminal.Gui for clean shutdown
 Application.Shutdown ();

+ 1 - 1
Terminal.Gui/Application.MainLoopSyncContext.cs

@@ -12,7 +12,7 @@ public static partial class Application
 
         public override void Post (SendOrPostCallback d, object state)
         {
-            MainLoop.AddIdle (
+            MainLoop?.AddIdle (
                               () =>
                               {
                                   d (state);

+ 228 - 168
Terminal.Gui/Application.cs

@@ -8,19 +8,12 @@ namespace Terminal.Gui;
 /// <summary>A static, singleton class representing the application. This class is the entry point for the application.</summary>
 /// <example>
 ///     <code>
-/// // A simple Terminal.Gui app that creates a window with a frame and title with 
-/// // 5 rows/columns of padding.
-/// Application.Init();
-/// var win = new Window ($"Example App ({Application.QuitKey} to quit)") {
-///   X = 5,
-///   Y = 5,
-///   Width = Dim.Fill (5),
-///   Height = Dim.Fill (5)
-/// };
-/// Application.Top.Add(win);
-/// Application.Run();
-/// Application.Shutdown();
-/// </code>
+///     Application.Init();
+///     var win = new Window ($"Example App ({Application.QuitKey} to quit)");
+///     Application.Run(win);
+///     win.Dispose();
+///     Application.Shutdown();
+///     </code>
 /// </example>
 /// <remarks>TODO: Flush this out.</remarks>
 public static partial class Application
@@ -75,6 +68,10 @@ public static partial class Application
                       .ToList ();
     }
 
+    // When `End ()` is called, it is possible `RunState.Toplevel` is a different object than `Top`.
+    // This variable is set in `End` in this case so that `Begin` correctly sets `Top`.
+    private static Toplevel _cachedRunStateToplevel;
+
     // IMPORTANT: Ensure all property/fields are reset here. See Init_ResetState_Resets_Properties unit test.
     // Encapsulate all setting of initial state for Application; Having
     // this in a function like this ensures we don't make mistakes in
@@ -88,13 +85,32 @@ public static partial class Application
         foreach (Toplevel t in _topLevels)
         {
             t.Running = false;
-            t.Dispose ();
+#if DEBUG_IDISPOSABLE
+
+            // Don't dispose the toplevels. It's up to caller dispose them
+            Debug.Assert (t.WasDisposed);
+#endif
         }
 
         _topLevels.Clear ();
         Current = null;
-        Top?.Dispose ();
+#if DEBUG_IDISPOSABLE
+
+        // Don't dispose the Top. It's up to caller dispose it
+        if (Top is { })
+        {
+            Debug.Assert (Top.WasDisposed);
+
+            // If End wasn't called _cachedRunStateToplevel may be null
+            if (_cachedRunStateToplevel is { })
+            {
+                Debug.Assert (_cachedRunStateToplevel.WasDisposed);
+                Debug.Assert (_cachedRunStateToplevel == Top);
+            }
+        }
+#endif
         Top = null;
+        _cachedRunStateToplevel = null;
 
         // MainLoop stuff
         MainLoop?.Dispose ();
@@ -161,12 +177,14 @@ public static partial class Application
     /// </para>
     /// <para>
     ///     <see cref="Shutdown"/> must be called when the application is closing (typically after
-    ///     <see cref="Run(Func{Exception, bool})"/> has returned) to ensure resources are cleaned up and terminal settings
+    ///     <see cref="Run(Func{Exception, bool}, ConsoleDriver)"/> has returned) to ensure resources are cleaned up and
+    ///     terminal settings
     ///     restored.
     /// </para>
     /// <para>
     ///     The <see cref="Run{T}(Func{Exception, bool}, ConsoleDriver)"/> function combines
-    ///     <see cref="Init(ConsoleDriver, string)"/> and <see cref="Run(Toplevel, Func{Exception, bool})"/> into a single
+    ///     <see cref="Init(ConsoleDriver, string)"/> and <see cref="Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/>
+    ///     into a single
     ///     call. An application cam use <see cref="Run{T}(Func{Exception, bool}, ConsoleDriver)"/> without explicitly calling
     ///     <see cref="Init(ConsoleDriver, string)"/>.
     /// </para>
@@ -179,7 +197,7 @@ public static partial class Application
     ///     <see cref="ConsoleDriver"/> to use. If neither <paramref name="driver"/> or <paramref name="driverName"/> are
     ///     specified the default driver for the platform will be used.
     /// </param>
-    public static void Init (ConsoleDriver driver = null, string driverName = null) { InternalInit (() => new Toplevel (), driver, driverName); }
+    public static void Init (ConsoleDriver driver = null, string driverName = null) { InternalInit (driver, driverName); }
 
     internal static bool _initialized;
     internal static int _mainThreadId = -1;
@@ -194,7 +212,6 @@ public static partial class Application
     // 
     // calledViaRunT: If false (default) all state will be reset. If true the state will not be reset.
     internal static void InternalInit (
-        Func<Toplevel> topLevelFactory,
         ConsoleDriver driver = null,
         string driverName = null,
         bool calledViaRunT = false
@@ -227,7 +244,8 @@ public static partial class Application
         // multiple times. We need to do this because some settings are only
         // valid after a Driver is loaded. In this cases we need just 
         // `Settings` so we can determine which driver to use.
-        Load (true);
+        // Don't reset, so we can inherit the theme from the previous run.
+        Load ();
         Apply ();
 
         // Ignore Configuration for ForceDriver if driverName is specified
@@ -292,12 +310,6 @@ public static partial class Application
 
         SynchronizationContext.SetSynchronizationContext (new MainLoopSyncContext ());
 
-        Top = topLevelFactory ();
-        Current = Top;
-
-        // Ensure Top's layout is up to date.
-        Current.SetRelativeLayout (Driver.Bounds);
-
         SupportedCultures = GetSupportedCultures ();
         _mainThreadId = Thread.CurrentThread.ManagedThreadId;
         _initialized = true;
@@ -332,11 +344,13 @@ public static partial class Application
     /// <summary>Shutdown an application initialized with <see cref="Init"/>.</summary>
     /// <remarks>
     ///     Shutdown must be called for every call to <see cref="Init"/> or
-    ///     <see cref="Application.Run(Toplevel, Func{Exception, bool})"/> to ensure all resources are cleaned up (Disposed)
+    ///     <see cref="Application.Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/> to ensure all resources are cleaned
+    ///     up (Disposed)
     ///     and terminal settings are restored.
     /// </remarks>
     public static void Shutdown ()
     {
+        // TODO: Throw an exception if Init hasn't been called.
         ResetState ();
         PrintJsonErrors ();
     }
@@ -369,59 +383,76 @@ public static partial class Application
     ///     The <see cref="RunState"/> handle that needs to be passed to the <see cref="End(RunState)"/> method upon
     ///     completion.
     /// </returns>
-    /// <param name="Toplevel">The <see cref="Toplevel"/> to prepare execution for.</param>
+    /// <param name="toplevel">The <see cref="Toplevel"/> to prepare execution for.</param>
     /// <remarks>
     ///     This method prepares the provided <see cref="Toplevel"/> for running with the focus, it adds this to the list
     ///     of <see cref="Toplevel"/>s, lays out the Subviews, focuses the first element, and draws the <see cref="Toplevel"/>
     ///     in the screen. This is usually followed by executing the <see cref="RunLoop"/> method, and then the
     ///     <see cref="End(RunState)"/> method upon termination which will undo these changes.
     /// </remarks>
-    public static RunState Begin (Toplevel Toplevel)
+    public static RunState Begin (Toplevel toplevel)
     {
-        if (Toplevel is null)
-        {
-            throw new ArgumentNullException (nameof (Toplevel));
-        }
+        ArgumentNullException.ThrowIfNull (toplevel);
 
 #if DEBUG_IDISPOSABLE
-        Debug.Assert (!Toplevel.WasDisposed);
+        Debug.Assert (!toplevel.WasDisposed);
+
+        if (_cachedRunStateToplevel is { } && _cachedRunStateToplevel != toplevel)
+        {
+            Debug.Assert (_cachedRunStateToplevel.WasDisposed);
+        }
 #endif
 
-        if (Toplevel.IsOverlappedContainer && OverlappedTop != Toplevel && OverlappedTop is { })
+        if (toplevel.IsOverlappedContainer && OverlappedTop != toplevel && OverlappedTop is { })
         {
             throw new InvalidOperationException ("Only one Overlapped Container is allowed.");
         }
 
-        // Ensure the mouse is ungrabed.
+        // Ensure the mouse is ungrabbed.
         MouseGrabView = null;
 
-        var rs = new RunState (Toplevel);
+        var rs = new RunState (toplevel);
 
         // View implements ISupportInitializeNotification which is derived from ISupportInitialize
-        if (!Toplevel.IsInitialized)
+        if (!toplevel.IsInitialized)
         {
-            Toplevel.BeginInit ();
-            Toplevel.EndInit ();
+            toplevel.BeginInit ();
+            toplevel.EndInit ();
         }
 
+#if DEBUG_IDISPOSABLE
+        if (Top is { } && toplevel != Top && !_topLevels.Contains (Top))
+        {
+            // This assertion confirm if the Top was already disposed
+            Debug.Assert (Top.WasDisposed);
+            Debug.Assert (Top == _cachedRunStateToplevel);
+        }
+#endif
+
         lock (_topLevels)
         {
-            // If Top was already initialized with Init, and Begin has never been called
-            // Top was not added to the Toplevels Stack. It will thus never get disposed.
-            // Clean it up here:
-            if (Top is { } && Toplevel != Top && !_topLevels.Contains (Top))
+            if (Top is { } && toplevel != Top && !_topLevels.Contains (Top))
             {
-                Top.Dispose ();
-                Top = null;
+                // If Top was already disposed and isn't on the Toplevels Stack,
+                // clean it up here if is the same as _cachedRunStateToplevel
+                if (Top == _cachedRunStateToplevel)
+                {
+                    Top = null;
+                }
+                else
+                {
+                    // Probably this will never hit
+                    throw new ObjectDisposedException (Top.GetType ().FullName);
+                }
             }
-            else if (Top is { } && Toplevel != Top && _topLevels.Contains (Top))
+            else if (OverlappedTop is { } && toplevel != Top && _topLevels.Contains (Top))
             {
-                Top.OnLeave (Toplevel);
+                Top.OnLeave (toplevel);
             }
 
             // BUGBUG: We should not depend on `Id` internally. 
             // BUGBUG: It is super unclear what this code does anyway.
-            if (string.IsNullOrEmpty (Toplevel.Id))
+            if (string.IsNullOrEmpty (toplevel.Id))
             {
                 var count = 1;
                 var id = (_topLevels.Count + count).ToString ();
@@ -432,42 +463,46 @@ public static partial class Application
                     id = (_topLevels.Count + count).ToString ();
                 }
 
-                Toplevel.Id = (_topLevels.Count + count).ToString ();
+                toplevel.Id = (_topLevels.Count + count).ToString ();
 
-                _topLevels.Push (Toplevel);
+                _topLevels.Push (toplevel);
             }
             else
             {
-                Toplevel dup = _topLevels.FirstOrDefault (x => x.Id == Toplevel.Id);
+                Toplevel dup = _topLevels.FirstOrDefault (x => x.Id == toplevel.Id);
 
                 if (dup is null)
                 {
-                    _topLevels.Push (Toplevel);
+                    _topLevels.Push (toplevel);
                 }
             }
 
             if (_topLevels.FindDuplicates (new ToplevelEqualityComparer ()).Count > 0)
             {
-                throw new ArgumentException ("There are duplicates Toplevels Id's");
+                throw new ArgumentException ("There are duplicates Toplevel IDs");
             }
         }
 
-        if (Top is null || Toplevel.IsOverlappedContainer)
+        if (Top is null || toplevel.IsOverlappedContainer)
         {
-            Top = Toplevel;
+            Top = toplevel;
         }
 
         var refreshDriver = true;
 
-        if (OverlappedTop == null
-            || Toplevel.IsOverlappedContainer
-            || (Current?.Modal == false && Toplevel.Modal)
-            || (Current?.Modal == false && !Toplevel.Modal)
-            || (Current?.Modal == true && Toplevel.Modal))
+        if (OverlappedTop is null
+            || toplevel.IsOverlappedContainer
+            || (Current?.Modal == false && toplevel.Modal)
+            || (Current?.Modal == false && !toplevel.Modal)
+            || (Current?.Modal == true && toplevel.Modal))
         {
-            if (Toplevel.Visible)
+            if (toplevel.Visible)
             {
-                Current = Toplevel;
+                Current?.OnDeactivate (toplevel);
+                Toplevel previousCurrent = Current;
+                Current = toplevel;
+                Current.OnActivate (previousCurrent);
+
                 SetCurrentOverlappedAsTop ();
             }
             else
@@ -476,13 +511,13 @@ public static partial class Application
             }
         }
         else if ((OverlappedTop != null
-                  && Toplevel != OverlappedTop
+                  && toplevel != OverlappedTop
                   && Current?.Modal == true
                   && !_topLevels.Peek ().Modal)
-                 || (OverlappedTop is { } && Toplevel != OverlappedTop && Current?.Running == false))
+                 || (OverlappedTop is { } && toplevel != OverlappedTop && Current?.Running == false))
         {
             refreshDriver = false;
-            MoveCurrent (Toplevel);
+            MoveCurrent (toplevel);
         }
         else
         {
@@ -490,113 +525,88 @@ public static partial class Application
             MoveCurrent (Current);
         }
 
-        //if (Toplevel.LayoutStyle == LayoutStyle.Computed) {
-        Toplevel.SetRelativeLayout (Driver.Bounds);
+        toplevel.SetRelativeLayout (Driver.Bounds);
 
-        //}
-
-        // BUGBUG: This call is likley not needed.
-        Toplevel.LayoutSubviews ();
-        Toplevel.PositionToplevels ();
-        Toplevel.FocusFirst ();
+        // BUGBUG: This call is likely not needed.
+        toplevel.LayoutSubviews ();
+        toplevel.PositionToplevels ();
+        toplevel.FocusFirst ();
 
         if (refreshDriver)
         {
-            OverlappedTop?.OnChildLoaded (Toplevel);
-            Toplevel.OnLoaded ();
-            Toplevel.SetNeedsDisplay ();
-            Toplevel.Draw ();
-            Toplevel.PositionCursor ();
+            OverlappedTop?.OnChildLoaded (toplevel);
+            toplevel.OnLoaded ();
+            toplevel.SetNeedsDisplay ();
+            toplevel.Draw ();
+            toplevel.PositionCursor ();
             Driver.Refresh ();
         }
 
-        NotifyNewRunState?.Invoke (Toplevel, new RunStateEventArgs (rs));
+        NotifyNewRunState?.Invoke (toplevel, new (rs));
 
         return rs;
     }
 
     /// <summary>
-    ///     Runs the application by calling <see cref="Run(Toplevel, Func{Exception, bool})"/> with the value of
-    ///     <see cref="Top"/>.
+    ///     Runs the application by creating a <see cref="Toplevel"/> object and calling
+    ///     <see cref="Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/>.
     /// </summary>
-    /// <remarks>See <see cref="Run(Toplevel, Func{Exception, bool})"/> for more details.</remarks>
-    public static void Run (Func<Exception, bool> errorHandler = null) { Run (Top, errorHandler); }
+    /// <remarks>
+    ///     <para>Calling <see cref="Init"/> first is not needed as this function will initialize the application.</para>
+    ///     <para>
+    ///         <see cref="Shutdown"/> must be called when the application is closing (typically after Run> has returned) to
+    ///         ensure resources are cleaned up and terminal settings restored.
+    ///     </para>
+    ///     <para>
+    ///         The caller is responsible for disposing the object returned by this method.
+    ///     </para>
+    /// </remarks>
+    /// <returns>The created <see cref="Toplevel"/> object. The caller is responsible for disposing this object.</returns>
+    public static Toplevel Run (Func<Exception, bool> errorHandler = null, ConsoleDriver driver = null) { return Run<Toplevel> (errorHandler, driver); }
 
     /// <summary>
-    ///     Runs the application by calling <see cref="Run(Toplevel, Func{Exception, bool})"/> with a new instance of the
-    ///     specified <see cref="Toplevel"/>-derived class.
+    ///     Runs the application by creating a <see cref="Toplevel"/>-derived object of type <c>T</c> and calling
+    ///     <see cref="Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/>.
+    /// </summary>
+    /// <remarks>
     ///     <para>Calling <see cref="Init"/> first is not needed as this function will initialize the application.</para>
     ///     <para>
     ///         <see cref="Shutdown"/> must be called when the application is closing (typically after Run> has returned) to
     ///         ensure resources are cleaned up and terminal settings restored.
     ///     </para>
-    /// </summary>
-    /// <remarks>See <see cref="Run(Toplevel, Func{Exception, bool})"/> for more details.</remarks>
+    ///     <para>
+    ///         The caller is responsible for disposing the object returned by this method.
+    ///     </para>
+    /// </remarks>
     /// <param name="errorHandler"></param>
     /// <param name="driver">
     ///     The <see cref="ConsoleDriver"/> to use. If not specified the default driver for the platform will
     ///     be used ( <see cref="WindowsDriver"/>, <see cref="CursesDriver"/>, or <see cref="NetDriver"/>). Must be
     ///     <see langword="null"/> if <see cref="Init"/> has already been called.
     /// </param>
-    public static void Run<T> (Func<Exception, bool> errorHandler = null, ConsoleDriver driver = null)
+    /// <returns>The created T object. The caller is responsible for disposing this object.</returns>
+    public static T Run<T> (Func<Exception, bool> errorHandler = null, ConsoleDriver driver = null)
         where T : Toplevel, new()
     {
-        if (_initialized)
-        {
-            // Init created Application.Top. If it hasn't been disposed...
-            if (Top is { })
-            {
-                Top.Dispose ();
-                Top = null;
-            }
+        var top = new T ();
 
-            if (Driver is { })
-            {
-                // Init() has been called and we have a driver, so just run the app.
-                // This Toplevel will get disposed in `Shutdown`
-                var top = new T ();
-                Type type = top.GetType ().BaseType;
+        Run (top, errorHandler, driver);
 
-                while (type != typeof (Toplevel) && type != typeof (object))
-                {
-                    type = type.BaseType;
-                }
-
-                if (type != typeof (Toplevel))
-                {
-                    throw new ArgumentException ($"{top.GetType ().Name} must be derived from TopLevel");
-                }
-
-                Run (top, errorHandler);
-            }
-            else
-            {
-                // This code path should be impossible because Init(null, null) will select the platform default driver
-                throw new InvalidOperationException (
-                                                     "Init() completed without a driver being set (this should be impossible); Run<T>() cannot be called."
-                                                    );
-            }
-        }
-        else
-        {
-            // Init() has NOT been called.
-            InternalInit (() => new T (), driver, null, true);
-            Run (Top, errorHandler);
-        }
+        return top;
     }
 
-    /// <summary>Runs the main loop on the given <see cref="Toplevel"/> container.</summary>
+    /// <summary>Runs the Application using the provided <see cref="Toplevel"/> view.</summary>
     /// <remarks>
     ///     <para>
     ///         This method is used to start processing events for the main application, but it is also used to run other
     ///         modal <see cref="View"/>s such as <see cref="Dialog"/> boxes.
     ///     </para>
     ///     <para>
-    ///         To make a <see cref="Run(Toplevel, Func{Exception, bool})"/> stop execution, call
+    ///         To make a <see cref="Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/> stop execution, call
     ///         <see cref="Application.RequestStop"/>.
     ///     </para>
     ///     <para>
-    ///         Calling <see cref="Run(Toplevel, Func{Exception, bool})"/> is equivalent to calling
+    ///         Calling <see cref="Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/> is equivalent to calling
     ///         <see cref="Begin(Toplevel)"/>, followed by <see cref="RunLoop(RunState)"/>, and then calling
     ///         <see cref="End(RunState)"/>.
     ///     </para>
@@ -607,6 +617,7 @@ public static partial class Application
     ///         <see cref="RunLoop(RunState)"/> method will only process any pending events, timers, idle handlers and then
     ///         return control immediately.
     ///     </para>
+    ///     <para>Calling <see cref="Init"/> first is not needed as this function will initialize the application.</para>
     ///     <para>
     ///         RELEASE builds only: When <paramref name="errorHandler"/> is <see langword="null"/> any exceptions will be
     ///         rethrown. Otherwise, if <paramref name="errorHandler"/> will be called. If <paramref name="errorHandler"/>
@@ -619,8 +630,34 @@ public static partial class Application
     ///     RELEASE builds only: Handler for any unhandled exceptions (resumes when returns true,
     ///     rethrows when null).
     /// </param>
-    public static void Run (Toplevel view, Func<Exception, bool> errorHandler = null)
+    /// <param name="driver">
+    ///     The <see cref="ConsoleDriver"/> to use. If not specified the default driver for the platform will
+    ///     be used ( <see cref="WindowsDriver"/>, <see cref="CursesDriver"/>, or <see cref="NetDriver"/>). Must be
+    ///     <see langword="null"/> if <see cref="Init"/> was called.
+    /// </param>
+    public static void Run (Toplevel view, Func<Exception, bool> errorHandler = null, ConsoleDriver driver = null)
     {
+        ArgumentNullException.ThrowIfNull (view);
+        
+        if (_initialized)
+        {
+            if (Driver is null)
+            {
+                // Disposing before throwing
+                view.Dispose ();
+
+                // This code path should be impossible because Init(null, null) will select the platform default driver
+                throw new InvalidOperationException (
+                                                     "Init() completed without a driver being set (this should be impossible); Run<T>() cannot be called."
+                                                    );
+            }
+        }
+        else
+        {
+            // Init() has NOT been called.
+            InternalInit (driver, null, true);
+        }
+
         var resume = true;
 
         while (resume)
@@ -636,6 +673,16 @@ public static partial class Application
             // by using NotifyStopRunState event.
             RunLoop (runState);
 
+            if (runState.Toplevel is null)
+            {
+#if DEBUG_IDISPOSABLE
+                Debug.Assert (_topLevels.Count == 0);
+#endif
+                runState.Dispose ();
+
+                return;
+            }
+
             if (!EndAfterFirstIteration)
             {
                 End (runState);
@@ -735,19 +782,12 @@ public static partial class Application
     /// <param name="state">The state returned by the <see cref="Begin(Toplevel)"/> method.</param>
     public static void RunLoop (RunState state)
     {
-        if (state is null)
-        {
-            throw new ArgumentNullException (nameof (state));
-        }
-
-        if (state.Toplevel is null)
-        {
-            throw new ObjectDisposedException ("state");
-        }
+        ArgumentNullException.ThrowIfNull (state);
+        ObjectDisposedException.ThrowIf (state.Toplevel is null, "state");
 
         var firstIteration = true;
 
-        for (state.Toplevel.Running = true; state.Toplevel.Running;)
+        for (state.Toplevel.Running = true; state.Toplevel?.Running == true;)
         {
             MainLoop.Running = true;
 
@@ -783,7 +823,7 @@ public static partial class Application
             }
 
             MainLoop.RunIteration ();
-            Iteration?.Invoke (null, new IterationEventArgs ());
+            Iteration?.Invoke (null, new ());
             EnsureModalOrVisibleAlwaysOnTop (state.Toplevel);
 
             if (state.Toplevel != Current)
@@ -798,6 +838,11 @@ public static partial class Application
 
         firstIteration = false;
 
+        if (Current == null)
+        {
+            return;
+        }
+
         if (state.Toplevel != Top && (Top.NeedsDisplay || Top.SubViewNeedsDisplay || Top.LayoutNeeded))
         {
             state.Toplevel.SetNeedsDisplay (state.Toplevel.Frame);
@@ -842,10 +887,10 @@ public static partial class Application
         }
     }
 
-    /// <summary>Stops running the most recent <see cref="Toplevel"/> or the <paramref name="top"/> if provided.</summary>
+    /// <summary>Stops the provided <see cref="Toplevel"/>, causing or the <paramref name="top"/> if provided.</summary>
     /// <param name="top">The <see cref="Toplevel"/> to stop.</param>
     /// <remarks>
-    ///     <para>This will cause <see cref="Application.Run(Func{Exception, bool})"/> to return.</para>
+    ///     <para>This will cause <see cref="Application.Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/> to return.</para>
     ///     <para>
     ///         Calling <see cref="Application.RequestStop"/> is equivalent to setting the <see cref="Toplevel.Running"/>
     ///         property on the currently running <see cref="Toplevel"/> to false.
@@ -880,7 +925,7 @@ public static partial class Application
                 return;
             }
 
-            ev = new ToplevelClosingEventArgs (top);
+            ev = new (top);
             top.OnClosing (ev);
 
             if (ev.Cancel)
@@ -968,7 +1013,7 @@ public static partial class Application
     {
         if (EndAfterFirstIteration)
         {
-            NotifyStopRunState?.Invoke (top, new ToplevelEventArgs (top));
+            NotifyStopRunState?.Invoke (top, new (top));
         }
     }
 
@@ -979,10 +1024,7 @@ public static partial class Application
     /// <param name="runState">The <see cref="RunState"/> returned by the <see cref="Begin(Toplevel)"/> method.</param>
     public static void End (RunState runState)
     {
-        if (runState is null)
-        {
-            throw new ArgumentNullException (nameof (runState));
-        }
+        ArgumentNullException.ThrowIfNull (runState);
 
         if (OverlappedTop is { })
         {
@@ -1024,6 +1066,11 @@ public static partial class Application
         }
         else
         {
+            if (_topLevels.Count > 1 && _topLevels.Peek () == OverlappedTop && OverlappedChildren.Any (t => t.Visible) is { })
+            {
+                OverlappedMoveNext ();
+            }
+
             Current = _topLevels.Peek ();
 
             if (_topLevels.Count == 1 && Current == OverlappedTop)
@@ -1040,8 +1087,18 @@ public static partial class Application
             Refresh ();
         }
 
-        // Do NOT dispose .Toplevel here. It was not created by
-        // Run, but Init or someone else.
+        // Don't dispose runState.Toplevel. It's up to caller dispose it
+        // If it's not the same as the current in the RunIteration,
+        // it will be fixed later in the next RunIteration.
+        if (OverlappedTop is { } && !_topLevels.Contains (OverlappedTop))
+        {
+            _cachedRunStateToplevel = OverlappedTop;
+        }
+        else
+        {
+            _cachedRunStateToplevel = runState.Toplevel;
+        }
+
         runState.Toplevel = null;
         runState.Dispose ();
     }
@@ -1061,10 +1118,13 @@ public static partial class Application
     public static Toplevel Top { get; private set; }
 
     /// <summary>
-    ///     The current <see cref="Toplevel"/> object. This is updated when
-    ///     <see cref="Application.Run(Func{Exception, bool})"/> enters and leaves to point to the current
+    ///     The current <see cref="Toplevel"/> object. This is updated in <see cref="Application.Begin"/> enters and leaves to
+    ///     point to the current
     ///     <see cref="Toplevel"/> .
     /// </summary>
+    /// <remarks>
+    ///     Only relevant in scenarios where <see cref="Toplevel.IsOverlappedContainer"/> is <see langword="true"/>.
+    /// </remarks>
     /// <value>The current.</value>
     public static Toplevel Current { get; private set; }
 
@@ -1182,7 +1242,7 @@ public static partial class Application
             && top != OverlappedTop
             && top != Current
             && Current?.Running == false
-            && !top.Running)
+            && top?.Running == false)
         {
             lock (_topLevels)
             {
@@ -1250,7 +1310,7 @@ public static partial class Application
             t.SetRelativeLayout (Rectangle.Empty with { Size = args.Size });
             t.LayoutSubviews ();
             t.PositionToplevels ();
-            t.OnSizeChanging (new SizeChangedEventArgs (args.Size));
+            t.OnSizeChanging (new (args.Size));
         }
 
         Refresh ();
@@ -1316,7 +1376,7 @@ public static partial class Application
 
         if (!OnUnGrabbingMouse (MouseGrabView))
         {
-            var view = MouseGrabView;
+            View view = MouseGrabView;
             MouseGrabView = null;
             OnUnGrabbedMouse (view);
         }
@@ -1355,7 +1415,7 @@ public static partial class Application
             return;
         }
 
-        GrabbedMouse?.Invoke (view, new ViewEventArgs (view));
+        GrabbedMouse?.Invoke (view, new (view));
     }
 
     private static void OnUnGrabbedMouse (View view)
@@ -1365,7 +1425,7 @@ public static partial class Application
             return;
         }
 
-        UnGrabbedMouse?.Invoke (view, new ViewEventArgs (view));
+        UnGrabbedMouse?.Invoke (view, new (view));
     }
 
 #nullable enable
@@ -1410,7 +1470,7 @@ public static partial class Application
             a.MouseEvent.View = view;
         }
 
-        MouseEvent?.Invoke (null, new MouseEventEventArgs (a.MouseEvent));
+        MouseEvent?.Invoke (null, new (a.MouseEvent));
 
         if (a.MouseEvent.Handled)
         {
@@ -1477,9 +1537,9 @@ public static partial class Application
 
         if (view is Adornment adornment)
         {
-            var frameLoc = adornment.ScreenToFrame (a.MouseEvent.X, a.MouseEvent.Y);
+            Point frameLoc = adornment.ScreenToFrame (a.MouseEvent.X, a.MouseEvent.Y);
 
-            me = new MouseEvent
+            me = new ()
             {
                 X = frameLoc.X,
                 Y = frameLoc.Y,
@@ -1492,7 +1552,7 @@ public static partial class Application
         {
             Point boundsPoint = view.ScreenToBounds (a.MouseEvent.X, a.MouseEvent.Y);
 
-            me = new MouseEvent
+            me = new ()
             {
                 X = boundsPoint.X,
                 Y = boundsPoint.Y,
@@ -1556,7 +1616,7 @@ public static partial class Application
             {
                 Key oldKey = _alternateForwardKey;
                 _alternateForwardKey = value;
-                OnAlternateForwardKeyChanged (new KeyChangedEventArgs (oldKey, value));
+                OnAlternateForwardKeyChanged (new (oldKey, value));
             }
         }
     }
@@ -1583,7 +1643,7 @@ public static partial class Application
             {
                 Key oldKey = _alternateBackwardKey;
                 _alternateBackwardKey = value;
-                OnAlternateBackwardKeyChanged (new KeyChangedEventArgs (oldKey, value));
+                OnAlternateBackwardKeyChanged (new (oldKey, value));
             }
         }
     }
@@ -1610,7 +1670,7 @@ public static partial class Application
             {
                 Key oldKey = _quitKey;
                 _quitKey = value;
-                OnQuitKeyChanged (new KeyChangedEventArgs (oldKey, value));
+                OnQuitKeyChanged (new (oldKey, value));
             }
         }
     }

+ 2 - 3
Terminal.Gui/Configuration/ConfigurationManager.cs

@@ -147,9 +147,8 @@ public static class ConfigurationManager
         {
             if (_settings is null)
             {
-                throw new InvalidOperationException (
-                                                     "ConfigurationManager has not been initialized. Call ConfigurationManager.Reset() before accessing the Settings property."
-                                                    );
+                // If Settings is null, we need to initialize it.
+                Reset ();
             }
 
             return _settings;

+ 9 - 2
Terminal.Gui/Configuration/SettingsScope.cs

@@ -3,6 +3,7 @@ using System.Diagnostics;
 using System.Reflection;
 using System.Text.Json;
 using System.Text.Json.Serialization;
+using static Terminal.Gui.SpinnerStyle;
 
 namespace Terminal.Gui;
 
@@ -44,7 +45,10 @@ public class SettingsScope : Scope<SettingsScope>
             Update (JsonSerializer.Deserialize<SettingsScope> (stream, _serializerOptions)!);
             OnUpdated ();
             Debug.WriteLine ($"ConfigurationManager: Read configuration from \"{source}\"");
-            Sources.Add (source);
+            if (!Sources.Contains (source))
+            {
+                Sources.Add (source);
+            }
 
             return this;
         }
@@ -70,7 +74,10 @@ public class SettingsScope : Scope<SettingsScope>
         if (!File.Exists (realPath))
         {
             Debug.WriteLine ($"ConfigurationManager: Configuration file \"{realPath}\" does not exist.");
-            Sources.Add (filePath);
+            if (!Sources.Contains (filePath))
+            {
+                Sources.Add (filePath);
+            }
 
             return this;
         }

+ 1 - 0
Terminal.Gui/FileServices/DefaultFileOperations.cs

@@ -162,6 +162,7 @@ public class DefaultFileOperations : IFileOperations
         dlg.AddButton (btnCancel);
 
         Application.Run (dlg);
+        dlg.Dispose ();
 
         result = tf.Text;
 

+ 16 - 1
Terminal.Gui/View/View.cs

@@ -427,9 +427,24 @@ public partial class View : Responder, ISupportInitializeNotification
     /// <value>The title.</value>
     public string Title
     {
-        get => _title;
+        get
+        {
+#if DEBUG_IDISPOSABLE
+            if (WasDisposed)
+            {
+                throw new ObjectDisposedException (GetType ().FullName);
+            }
+#endif
+            return _title;
+        }
         set
         {
+#if DEBUG_IDISPOSABLE
+            if (WasDisposed)
+            {
+                throw new ObjectDisposedException (GetType ().FullName);
+            }
+#endif
             if (value == _title)
             {
                 return;

+ 37 - 1
Terminal.Gui/Views/Dialog.cs

@@ -9,7 +9,7 @@ namespace Terminal.Gui;
 /// </summary>
 /// <remarks>
 ///     To run the <see cref="Dialog"/> modally, create the <see cref="Dialog"/>, and pass it to
-///     <see cref="Application.Run(Func{Exception, bool})"/>. This will execute the dialog until it terminates via the
+///     <see cref="Application.Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/>. This will execute the dialog until it terminates via the
 ///     [ESC] or [CTRL-Q] key, or when one of the views or buttons added to the dialog calls
 ///     <see cref="Application.RequestStop"/>.
 /// </remarks>
@@ -68,9 +68,45 @@ public class Dialog : Window
         Modal = true;
         ButtonAlignment = DefaultButtonAlignment;
 
+        AddCommand (Command.QuitToplevel, () =>
+                                          {
+                                              Canceled = true;
+                                              RequestStop ();
+                                              return true;
+                                          });
         KeyBindings.Add (Key.Esc, Command.QuitToplevel);
     }
 
+
+    private bool _canceled;
+
+    /// <summary>Gets a value indicating whether the <see cref="Dialog"/> was canceled.</summary>
+    /// <remarks>The default value is <see langword="true"/>.</remarks>
+    public bool Canceled
+    {
+        get
+        {
+#if DEBUG_IDISPOSABLE
+            if (WasDisposed)
+            {
+                throw new ObjectDisposedException (GetType ().FullName);
+            }
+#endif
+            return _canceled;
+        }
+        set
+        {
+#if DEBUG_IDISPOSABLE
+            if (WasDisposed)
+            {
+                throw new ObjectDisposedException (GetType ().FullName);
+            }
+#endif
+            _canceled = value;
+            return;
+        }
+    }
+
     /// <summary>Determines how the <see cref="Dialog"/> <see cref="Button"/>s are aligned along the bottom of the dialog.</summary>
     public ButtonAlignments ButtonAlignment { get; set; }
 

+ 9 - 6
Terminal.Gui/Views/FileDialog.cs

@@ -60,6 +60,9 @@ public class FileDialog : Dialog
     /// <remarks>This overload is mainly useful for testing.</remarks>
     internal FileDialog (IFileSystem fileSystem)
     {
+        // Assume canceled
+        Canceled = true;
+
         _fileSystem = fileSystem;
         Style = new FileDialogStyle (fileSystem);
 
@@ -83,7 +86,10 @@ public class FileDialog : Dialog
                                   NavigateIf (k, KeyCode.CursorUp, _tableView);
                                   NavigateIf (k, KeyCode.CursorRight, _btnOk);
                               };
-        _btnCancel.Accept += (s, e) => { Application.RequestStop (); };
+        _btnCancel.Accept += (s, e) => {
+                                 Canceled = true;
+                                 Application.RequestStop ();
+                             };
 
         _btnUp = new Button { X = 0, Y = 1, NoPadding = true };
         _btnUp.Text = GetUpButtonText ();
@@ -314,9 +320,6 @@ public class FileDialog : Dialog
         set => _tableView.MultiSelect = value;
     }
 
-    /// <summary>Gets a value indicating whether the <see cref="FileDialog"/> was closed without confirming a selection.</summary>
-    public bool Canceled { get; private set; } = true;
-
     /// <summary>The UI selected <see cref="IAllowedType"/> from combo box. May be null.</summary>
     public IAllowedType CurrentFilter { get; private set; }
 
@@ -336,7 +339,7 @@ public class FileDialog : Dialog
 
     /// <summary>
     ///     Gets all files/directories selected or an empty collection <see cref="AllowsMultipleSelection"/> is
-    ///     <see langword="false"/> or <see cref="Canceled"/>.
+    ///     <see langword="false"/> or <see cref="CancelSearch"/>.
     /// </summary>
     /// <remarks>If selecting only a single file/directory then you should use <see cref="Path"/> instead.</remarks>
     public IReadOnlyList<string> MultiSelected { get; private set; }
@@ -356,7 +359,7 @@ public class FileDialog : Dialog
 
     /// <summary>
     ///     Gets or Sets the selected path in the dialog. This is the result that should be used if
-    ///     <see cref="AllowsMultipleSelection"/> is off and <see cref="Canceled"/> is true.
+    ///     <see cref="AllowsMultipleSelection"/> is off and <see cref="CancelSearch"/> is true.
     /// </summary>
     public string Path
     {

+ 9 - 4
Terminal.Gui/Views/ListView.cs

@@ -429,7 +429,7 @@ public class ListView : View
     /// <returns></returns>
     public virtual bool MoveDown ()
     {
-        if (_source.Count == 0)
+        if (_source is null || _source.Count == 0)
         {
             // Do we set lastSelectedItem to -1 here?
             return false; //Nothing for us to move to
@@ -479,7 +479,7 @@ public class ListView : View
     /// <returns></returns>
     public virtual bool MoveEnd ()
     {
-        if (_source.Count > 0 && _selected != _source.Count - 1)
+        if (_source is { Count: > 0 } && _selected != _source.Count - 1)
         {
             _selected = _source.Count - 1;
 
@@ -517,6 +517,11 @@ public class ListView : View
     /// <returns></returns>
     public virtual bool MovePageDown ()
     {
+        if (_source is null)
+        {
+            return true;
+        }
+
         int n = _selected + Bounds.Height;
 
         if (n >= _source.Count)
@@ -570,7 +575,7 @@ public class ListView : View
     /// <returns></returns>
     public virtual bool MoveUp ()
     {
-        if (_source.Count == 0)
+        if (_source is null || _source.Count == 0)
         {
             // Do we set lastSelectedItem to -1 here?
             return false; //Nothing for us to move to
@@ -697,7 +702,7 @@ public class ListView : View
     /// <returns><see langword="true"/> if the <see cref="OpenSelectedItem"/> event was fired.</returns>
     public bool OnOpenSelectedItem ()
     {
-        if (_source.Count <= _selected || _selected < 0 || OpenSelectedItem is null)
+        if (_source is null || _source.Count <= _selected || _selected < 0 || OpenSelectedItem is null)
         {
             return false;
         }

+ 3 - 0
Terminal.Gui/Views/Menu/ContextMenu.cs

@@ -116,6 +116,7 @@ public sealed class ContextMenu : IDisposable
         if (_container is { })
         {
             _container.Closing -= Container_Closing;
+            _container.Deactivate -= Container_Deactivate;
         }
     }
 
@@ -142,6 +143,7 @@ public sealed class ContextMenu : IDisposable
 
         _container = Application.Current;
         _container.Closing += Container_Closing;
+        _container.Deactivate += Container_Deactivate;
         Rectangle frame = Application.Driver.Bounds;
         Point position = Position;
 
@@ -217,6 +219,7 @@ public sealed class ContextMenu : IDisposable
         _menuBar.OpenMenu ();
     }
 
+    private void Container_Deactivate (object sender, ToplevelEventArgs e) { Hide (); }
     private void Container_Closing (object sender, ToplevelClosingEventArgs obj) { Hide (); }
     private void MenuBar_MenuAllClosed (object sender, EventArgs e) { Dispose (); }
 }

+ 1 - 0
Terminal.Gui/Views/MessageBox.cs

@@ -468,6 +468,7 @@ public static class MessageBox
 
         // Run the modal; do not shutdown the mainloop driver when done
         Application.Run (d);
+        d.Dispose ();
 
         return Clicked;
     }

+ 1 - 1
Terminal.Gui/Views/OpenDialog.cs

@@ -36,7 +36,7 @@ public enum OpenMode
 ///     </para>
 ///     <para>
 ///         To use, create an instance of <see cref="OpenDialog"/>, and pass it to
-///         <see cref="Application.Run(Func{Exception, bool})"/>. This will run the dialog modally, and when this returns,
+///         <see cref="Application.Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/>. This will run the dialog modally, and when this returns,
 ///         the list of files will be available on the <see cref="FilePaths"/> property.
 ///     </para>
 ///     <para>To select more than one file, users can use the spacebar, or control-t.</para>

+ 1 - 1
Terminal.Gui/Views/SaveDialog.cs

@@ -17,7 +17,7 @@ namespace Terminal.Gui;
 /// <remarks>
 ///     <para>
 ///         To use, create an instance of <see cref="SaveDialog"/>, and pass it to
-///         <see cref="Application.Run(Func{Exception, bool})"/>. This will run the dialog modally, and when this returns,
+///         <see cref="Application.Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/>. This will run the dialog modally, and when this returns,
 ///         the <see cref="FileName"/>property will contain the selected file name or null if the user canceled.
 ///     </para>
 /// </remarks>

+ 1 - 1
Terminal.Gui/Views/TableView/TableView.cs

@@ -2119,7 +2119,7 @@ public class TableView : View
     )
     {
         // if the column index provided is out of bounds
-        if (columnIndex < 0 || columnIndex >= table.Columns)
+        if (table is null || columnIndex < 0 || columnIndex >= table.Columns)
         {
             idx = columnIndex;
 

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

@@ -9,7 +9,7 @@ namespace Terminal.Gui;
 /// <remarks>
 ///     <para>
 ///         Toplevels can run as modal (popup) views, started by calling
-///         <see cref="Application.Run(Toplevel, Func{Exception,bool})"/>. They return control to the caller when
+///         <see cref="Application.Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/>. They return control to the caller when
 ///         <see cref="Application.RequestStop(Toplevel)"/> has been called (which sets the <see cref="Toplevel.Running"/>
 ///         property to <c>false</c>).
 ///     </para>
@@ -17,7 +17,7 @@ namespace Terminal.Gui;
 ///         A Toplevel is created when an application initializes Terminal.Gui by calling <see cref="Application.Init"/>.
 ///         The application Toplevel can be accessed via <see cref="Application.Top"/>. Additional Toplevels can be created
 ///         and run (e.g. <see cref="Dialog"/>s. To run a Toplevel, create the <see cref="Toplevel"/> and call
-///         <see cref="Application.Run(Toplevel, Func{Exception,bool})"/>.
+///         <see cref="Application.Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/>.
 ///     </para>
 /// </remarks>
 public partial class Toplevel : View
@@ -452,7 +452,7 @@ public partial class Toplevel : View
     /// <inheritdoc/>
     public override void Remove (View view)
     {
-        if (this is Toplevel Toplevel && Toplevel.MenuBar is { })
+        if (this is Toplevel { MenuBar: { } })
         {
             RemoveMenuStatusBar (view);
         }
@@ -803,7 +803,7 @@ public partial class Toplevel : View
     {
         if (Application.OverlappedTop is { })
         {
-            Application.OverlappedTop.RequestStop ();
+            RequestStop (this);
         }
         else
         {

+ 1 - 1
Terminal.Gui/Views/Wizard/Wizard.cs

@@ -143,7 +143,7 @@ public class Wizard : Dialog
     ///             <description>Add the Wizard to a containing view with <see cref="View.Add(View)"/>.</description>
     ///         </item>
     ///     </list>
-    ///     If a non-Modal Wizard is added to the application after <see cref="Application.Run(Func{Exception, bool})"/> has
+    ///     If a non-Modal Wizard is added to the application after <see cref="Application.Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/> has
     ///     been called the first step must be explicitly set by setting <see cref="CurrentStep"/> to
     ///     <see cref="GetNextStep()"/>:
     ///     <code>

+ 1 - 0
UICatalog/KeyBindingsDialog.cs

@@ -82,6 +82,7 @@ internal class KeyBindingsDialog : Dialog
                            Application.RequestStop ();
                        };
         Application.Run (dlg);
+        dlg.Dispose ();
 
         if (key.HasValue)
         {

+ 80 - 55
UICatalog/Scenario.cs

@@ -45,7 +45,7 @@ namespace UICatalog;
 ///     </para>
 ///     <para>
 ///         The UI Catalog program uses reflection to find all scenarios and adds them to the ListViews. Press ENTER to
-///         run the selected scenario. Press the default quit key to quit.	/
+///         run the selected scenario. Press the default quit key to quit.
 ///     </para>
 /// </summary>
 /// <example>
@@ -77,19 +77,6 @@ public class Scenario : IDisposable
     public string TopLevelColorScheme = "Base";
     private bool _disposedValue;
 
-    /// <summary>
-    ///     The Window for the <see cref="Scenario"/>. This should be set to <see cref="Terminal.Gui.Application.Top"/> in
-    ///     most cases.
-    /// </summary>
-    public Window Win { get; set; }
-
-    public void Dispose ()
-    {
-        // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
-        Dispose (true);
-        GC.SuppressFinalize (this);
-    }
-
     /// <summary>
     ///     Helper function to get the list of categories a <see cref="Scenario"/> belongs to (defined in
     ///     <see cref="ScenarioCategory"/>)
@@ -130,7 +117,28 @@ public class Scenario : IDisposable
     }
 
     /// <summary>
-    ///     Helper that provides the default <see cref="Terminal.Gui.Window"/> implementation with a frame and label
+    ///     Called by UI Catalog to run the <see cref="Scenario"/>. This is the main entry point for the <see cref="Scenario"/>
+    ///     .
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         Scenario developers are encouraged to override this method as the primary way of authoring a new
+    ///         scenario.
+    ///     </para>
+    ///     <para>
+    ///         The base implementation calls <see cref="Init"/>, <see cref="Setup"/>, and <see cref="Run"/>.
+    ///     </para>
+    /// </remarks>
+    public virtual void Main ()
+    {
+        Init ();
+        Setup ();
+        Run ();
+    }
+
+    /// <summary>
+    ///     Helper that calls <see cref="Application.Init"/> and creates the default <see cref="Terminal.Gui.Window"/>
+    ///     implementation with a frame and label
     ///     showing the name of the <see cref="Scenario"/> and logic to exit back to the Scenario picker UI. Override
     ///     <see cref="Init"/> to provide any <see cref="Terminal.Gui.Toplevel"/> behavior needed.
     /// </summary>
@@ -144,6 +152,7 @@ public class Scenario : IDisposable
     ///         creating any views or calling other Terminal.Gui APIs.
     ///     </para>
     /// </remarks>
+    [ObsoleteAttribute ("This method is obsolete and will be removed in v2. Use Main instead.", false)]
     public virtual void Init ()
     {
         Application.Init ();
@@ -151,7 +160,9 @@ public class Scenario : IDisposable
         ConfigurationManager.Themes.Theme = Theme;
         ConfigurationManager.Apply ();
 
-        Win = new Window
+        Top = new ();
+
+        Win = new ()
         {
             Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}",
             X = 0,
@@ -160,12 +171,9 @@ public class Scenario : IDisposable
             Height = Dim.Fill (),
             ColorScheme = Colors.ColorSchemes [TopLevelColorScheme]
         };
-        Application.Top.Add (Win);
+        Top.Add (Win);
     }
 
-    /// <summary>Stops the scenario. Override to change shutdown behavior for the <see cref="Scenario"/>.</summary>
-    public virtual void RequestStop () { Application.RequestStop (); }
-
     /// <summary>
     ///     Runs the <see cref="Scenario"/>. Override to start the <see cref="Scenario"/> using a <see cref="Toplevel"/>
     ///     different than `Top`.
@@ -174,53 +182,78 @@ public class Scenario : IDisposable
     ///     Overrides that do not call the base.<see cref="Run"/>, must call <see cref="Application.Shutdown"/> before
     ///     returning.
     /// </remarks>
+    [ObsoleteAttribute ("This method is obsolete and will be removed in v2. Use Main instead.", false)]
     public virtual void Run ()
     {
-        // Must explicit call Application.Shutdown method to shutdown.
-        Application.Run (Application.Top);
+        // Must explicitly call Application.Shutdown method to shutdown.
+        Application.Run (Top);
     }
 
     /// <summary>Override this to implement the <see cref="Scenario"/> setup logic (create controls, etc...).</summary>
     /// <remarks>This is typically the best place to put scenario logic code.</remarks>
+    [ObsoleteAttribute ("This method is obsolete and will be removed in v2. Use Main instead.", false)]
     public virtual void Setup () { }
 
+    /// <summary>
+    ///     The Toplevel for the <see cref="Scenario"/>. This should be set to <see cref="Terminal.Gui.Application.Top"/>.
+    /// </summary>
+
+    //[ObsoleteAttribute ("This property is obsolete and will be removed in v2. Use Main instead.", false)]
+    public Toplevel Top { get; set; }
+
     /// <summary>Gets the Scenario Name + Description with the Description padded based on the longest known Scenario name.</summary>
     /// <returns></returns>
     public override string ToString () { return $"{GetName ().PadRight (_maxScenarioNameLen)}{GetDescription ()}"; }
 
+    /// <summary>
+    ///     The Window for the <see cref="Scenario"/>. This should be set to <see cref="Terminal.Gui.Application.Top"/> in
+    ///     most cases.
+    /// </summary>
+
+    //[ObsoleteAttribute ("This property is obsolete and will be removed in v2. Use Main instead.", false)]
+    public Window Win { get; set; }
+
+    #region IDispose
+
+    public void Dispose ()
+    {
+        // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
+        Dispose (true);
+        GC.SuppressFinalize (this);
+    }
+
     protected virtual void Dispose (bool disposing)
     {
         if (!_disposedValue)
         {
             if (disposing)
             {
-                // TODO: dispose managed state (managed objects)
+                Top?.Dispose ();
+                Win?.Dispose ();
             }
 
-            // TODO: free unmanaged resources (unmanaged objects) and override finalizer
-            // TODO: set large fields to null
             _disposedValue = true;
         }
     }
 
+    #endregion IDispose
+
     /// <summary>Returns a list of all Categories set by all of the <see cref="Scenario"/>s defined in the project.</summary>
     internal static List<string> GetAllCategories ()
     {
         List<string> categories = new ();
 
-        foreach (Type type in typeof (Scenario).Assembly.GetTypes ()
-                                               .Where (
-                                                       myType => myType.IsClass
-                                                                 && !myType.IsAbstract
-                                                                 && myType.IsSubclassOf (typeof (Scenario))
-                                                      ))
-        {
-            List<System.Attribute> attrs = System.Attribute.GetCustomAttributes (type).ToList ();
-
-            categories = categories
-                         .Union (attrs.Where (a => a is ScenarioCategory).Select (a => ((ScenarioCategory)a).Name))
-                         .ToList ();
-        }
+        categories = typeof (Scenario).Assembly.GetTypes ()
+                                      .Where (
+                                              myType => myType.IsClass
+                                                        && !myType.IsAbstract
+                                                        && myType.IsSubclassOf (typeof (Scenario)))
+                                      .Select (type => System.Attribute.GetCustomAttributes (type).ToList ())
+                                      .Aggregate (
+                                                  categories,
+                                                  (current, attrs) => current
+                                                                      .Union (attrs.Where (a => a is ScenarioCategory).Select (a => ((ScenarioCategory)a).Name))
+                                                                      .ToList ());
 
         // Sort
         categories = categories.OrderBy (c => c).ToList ();
@@ -233,13 +266,8 @@ public class Scenario : IDisposable
 
     /// <summary>Defines the category names used to catagorize a <see cref="Scenario"/></summary>
     [AttributeUsage (AttributeTargets.Class, AllowMultiple = true)]
-    public class ScenarioCategory : System.Attribute
+    public class ScenarioCategory (string Name) : System.Attribute
     {
-        public ScenarioCategory (string Name) { this.Name = Name; }
-
-        /// <summary>Category Name</summary>
-        public string Name { get; set; }
-
         /// <summary>Static helper function to get the <see cref="Scenario"/> Categories given a Type</summary>
         /// <param name="t"></param>
         /// <returns>list of category names</returns>
@@ -256,23 +284,17 @@ public class Scenario : IDisposable
         /// <param name="t"></param>
         /// <returns>Name of the category</returns>
         public static string GetName (Type t) { return ((ScenarioCategory)GetCustomAttributes (t) [0]).Name; }
+
+        /// <summary>Category Name</summary>
+        public string Name { get; set; } = Name;
     }
 
     /// <summary>Defines the metadata (Name and Description) for a <see cref="Scenario"/></summary>
     [AttributeUsage (AttributeTargets.Class)]
-    public class ScenarioMetadata : System.Attribute
+    public class ScenarioMetadata (string Name, string Description) : System.Attribute
     {
-        public ScenarioMetadata (string Name, string Description)
-        {
-            this.Name = Name;
-            this.Description = Description;
-        }
-
         /// <summary><see cref="Scenario"/> Description</summary>
-        public string Description { get; set; }
-
-        /// <summary><see cref="Scenario"/> Name</summary>
-        public string Name { get; set; }
+        public string Description { get; set; } = Description;
 
         /// <summary>Static helper function to get the <see cref="Scenario"/> Description given a Type</summary>
         /// <param name="t"></param>
@@ -283,5 +305,8 @@ public class Scenario : IDisposable
         /// <param name="t"></param>
         /// <returns></returns>
         public static string GetName (Type t) { return ((ScenarioMetadata)GetCustomAttributes (t) [0]).Name; }
+
+        /// <summary><see cref="Scenario"/> Name</summary>
+        public string Name { get; set; } = Name;
     }
 }

+ 5 - 4
UICatalog/Scenarios/ASCIICustomButton.cs

@@ -50,8 +50,9 @@ public class ASCIICustomButtonTest : Scenario
                                 )
             ]
         };
-        Application.Top.Add (menu, _scrollViewTestWindow);
-        Application.Run ();
+        Top = new ();
+        Top.Add (menu, _scrollViewTestWindow);
+        Application.Run (Top);
     }
 
     public override void Run () { }
@@ -60,9 +61,9 @@ public class ASCIICustomButtonTest : Scenario
     {
         _smallerWindow = (bool)(_miSmallerWindow.Checked = !_miSmallerWindow.Checked);
         _scrollViewTestWindow.Dispose ();
-        Application.Top.Remove (_scrollViewTestWindow);
+        Top.Remove (_scrollViewTestWindow);
         _scrollViewTestWindow = new ScrollViewTestWindow ();
-        Application.Top.Add (_scrollViewTestWindow);
+        Top.Add (_scrollViewTestWindow);
     }
 
     public class ASCIICustomButton : Button

+ 7 - 8
UICatalog/Scenarios/AdornmentExperiments.cs

@@ -8,33 +8,32 @@ public class AdornmentExperiments : Scenario
 {
     private ViewDiagnosticFlags _diagnosticFlags;
 
+    private View _frameView;
+
     public override void Init ()
     {
         Application.Init ();
         ConfigurationManager.Themes.Theme = Theme;
         ConfigurationManager.Apply ();
-        Application.Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
+        Top = new ();
+        Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
 
         _diagnosticFlags = View.Diagnostics;
         //View.Diagnostics = ViewDiagnosticFlags.MouseEnter;
-    }
 
-    private View _frameView;
-    public override void Setup ()
-    {
         _frameView = new View ()
         {
             Title = "Frame View",
             X = 0,
             Y = 0,
-            Width = Dim.Percent(90),
+            Width = Dim.Percent (90),
             Height = Dim.Percent (90),
             CanFocus = true,
         };
-        Application.Top.Add (_frameView);
+        Top.Add (_frameView);
         _frameView.Initialized += FrameView_Initialized;
 
-        Application.Top.Closed += (s, e) => View.Diagnostics = _diagnosticFlags;
+        Top.Closed += (s, e) => View.Diagnostics = _diagnosticFlags;
     }
 
     private void FrameView_Initialized (object sender, System.EventArgs e)

+ 5 - 2
UICatalog/Scenarios/Adornments.cs

@@ -18,7 +18,8 @@ public class Adornments : Scenario
         Application.Init ();
         ConfigurationManager.Themes.Theme = Theme;
         ConfigurationManager.Apply ();
-        Application.Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
+        Top = new ();
+        Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
 
         var view = new Window { Title = "The _Window" };
         var tf1 = new TextField { Width = 10, Text = "TextField" };
@@ -117,9 +118,11 @@ public class Adornments : Scenario
 #endif
                             };
 
-        Application.Top.Closed += (s, e) => View.Diagnostics = _diagnosticFlags;
+        Top.Closed += (s, e) => View.Diagnostics = _diagnosticFlags;
 
         Application.Run (editor);
+        editor.Dispose ();
+
         Application.Shutdown ();
     }
 

+ 6 - 8
UICatalog/Scenarios/AllViewsTester.cs

@@ -46,11 +46,9 @@ public class AllViewsTester : Scenario
         Application.Init ();
         ConfigurationManager.Themes.Theme = Theme;
         ConfigurationManager.Apply ();
-        Application.Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
-    }
+        Top = new ();
+        Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
 
-    public override void Setup ()
-    {
         var statusBar = new StatusBar (
                                        new StatusItem []
                                        {
@@ -66,7 +64,7 @@ public class AllViewsTester : Scenario
                                                 {
                                                     View.Diagnostics ^=
                                                         ViewDiagnosticFlags.Ruler;
-                                                    Application.Top.SetNeedsDisplay ();
+                                                    Top.SetNeedsDisplay ();
                                                 }
                                                ),
                                            new (
@@ -76,12 +74,12 @@ public class AllViewsTester : Scenario
                                                 {
                                                     View.Diagnostics ^=
                                                         ViewDiagnosticFlags.Padding;
-                                                    Application.Top.SetNeedsDisplay ();
+                                                    Top.SetNeedsDisplay ();
                                                 }
                                                )
                                        }
                                       );
-        Application.Top.Add (statusBar);
+        Top.Add (statusBar);
 
         _viewClasses = GetAllViewClassesCollection ()
                        .OrderBy (t => t.Name)
@@ -286,7 +284,7 @@ public class AllViewsTester : Scenario
             ColorScheme = Colors.ColorSchemes ["Dialog"]
         };
 
-        Application.Top.Add (_leftPane, _settingsPane, _hostPane);
+        Top.Add (_leftPane, _settingsPane, _hostPane);
 
         _curView = CreateClass (_viewClasses.First ().Value);
     }

+ 18 - 5
UICatalog/Scenarios/Animation.cs

@@ -17,22 +17,31 @@ public class Animation : Scenario
 {
     private bool _isDisposed;
 
-    public override void Setup ()
+    public override void Main ()
     {
-        base.Setup ();
+        Application.Init();
+
+        var win = new Window
+        {
+            Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}",
+            X = 0,
+            Y = 0,
+            Width = Dim.Fill (),
+            Height = Dim.Fill (),
+        };
 
         var imageView = new ImageView { Width = Dim.Fill (), Height = Dim.Fill () - 2 };
 
-        Win.Add (imageView);
+        win.Add (imageView);
 
         var lbl = new Label { Y = Pos.AnchorEnd (2), Text = "Image by Wikiscient" };
-        Win.Add (lbl);
+        win.Add (lbl);
 
         var lbl2 = new Label
         {
             Y = Pos.AnchorEnd (1), Text = "https://commons.wikimedia.org/wiki/File:Spinning_globe.gif"
         };
-        Win.Add (lbl2);
+        win.Add (lbl2);
 
         DirectoryInfo dir;
 
@@ -78,6 +87,10 @@ public class Animation : Scenario
                       }
                   }
                  );
+
+        Application.Run (win);
+        win.Dispose ();
+        Application.Shutdown ();
     }
 
     protected override void Dispose (bool disposing)

+ 82 - 41
UICatalog/Scenarios/BackgroundWorkerCollection.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
+using System.Diagnostics;
 using System.Threading;
 using Terminal.Gui;
 
@@ -13,13 +14,25 @@ namespace UICatalog.Scenarios;
 [ScenarioCategory ("Controls")]
 public class BackgroundWorkerCollection : Scenario
 {
-    public override void Run () { Application.Run<OverlappedMain> (); }
+    public override void Main ()
+    {
+        Application.Run<OverlappedMain> ().Dispose ();
+
+#if DEBUG_IDISPOSABLE
+        if (Application.OverlappedChildren is { })
+        {
+            Debug.Assert (Application.OverlappedChildren?.Count == 0);
+            Debug.Assert (Application.Top == Application.OverlappedTop);
+        }
+#endif
+
+        Application.Shutdown ();
+    }
 
     private class OverlappedMain : Toplevel
     {
         private readonly MenuBar _menu;
-        private readonly WorkerApp _workerApp;
-        private bool _canOpenWorkerApp;
+        private WorkerApp _workerApp;
 
         public OverlappedMain ()
         {
@@ -28,6 +41,8 @@ public class BackgroundWorkerCollection : Scenario
             IsOverlappedContainer = true;
 
             _workerApp = new WorkerApp { Visible = false };
+            _workerApp.Border.Thickness = new (0, 1, 0, 0);
+            _workerApp.Border.LineStyle = LineStyle.Dashed;
 
             _menu = new MenuBar
             {
@@ -90,29 +105,21 @@ public class BackgroundWorkerCollection : Scenario
                                           );
             Add (statusBar);
 
+            Ready += OverlappedMain_Ready;
             Activate += OverlappedMain_Activate;
             Deactivate += OverlappedMain_Deactivate;
-
-            Closed += OverlappedMain_Closed;
-
-            Application.Iteration += (s, a) =>
-                                     {
-                                         if (_canOpenWorkerApp && !_workerApp.Running && Application.OverlappedTop.Running)
-                                         {
-                                             Application.Run (_workerApp);
-                                         }
-                                     };
         }
 
-        private void Menu_MenuOpening (object sender, MenuOpeningEventArgs menu)
+        private void OverlappedMain_Ready (object sender, EventArgs e)
         {
-            if (!_canOpenWorkerApp)
+            if (_workerApp?.Running == false)
             {
-                _canOpenWorkerApp = true;
-
-                return;
+                Application.Run (_workerApp);
             }
+        }
 
+        private void Menu_MenuOpening (object sender, MenuOpeningEventArgs menu)
+        {
             if (menu.CurrentMenu.Title == "_Window")
             {
                 menu.NewMenuBarItem = OpenedWindows ();
@@ -165,15 +172,16 @@ public class BackgroundWorkerCollection : Scenario
             return new MenuBarItem ("_Window", new List<MenuItem []> { menuItems.ToArray () });
         }
 
-        private void OverlappedMain_Activate (object sender, ToplevelEventArgs top) { _workerApp.WriteLog ($"{top.Toplevel.Data} activate."); }
+        private void OverlappedMain_Activate (object sender, ToplevelEventArgs top)
+        {
+            _workerApp?.WriteLog ($"{(top.Toplevel is null ? ((Toplevel)sender).Data : top.Toplevel.Data)} activate.");
+        }
 
-        private void OverlappedMain_Closed (object sender, ToplevelEventArgs e)
+        private void OverlappedMain_Deactivate (object sender, ToplevelEventArgs top)
         {
-            _workerApp.Dispose ();
-            Dispose ();
+            _workerApp?.WriteLog ($"{top.Toplevel.Data} deactivate.");
         }
 
-        private void OverlappedMain_Deactivate (object sender, ToplevelEventArgs top) { _workerApp.WriteLog ($"{top.Toplevel.Data} deactivate."); }
         private void Quit () { RequestStop (); }
 
         private MenuBarItem View ()
@@ -208,6 +216,15 @@ public class BackgroundWorkerCollection : Scenario
                                     new List<MenuItem []> { menuItems.Count == 0 ? new MenuItem [] { } : menuItems.ToArray () }
                                    );
         }
+
+        /// <inheritdoc />
+        protected override void Dispose (bool disposing)
+        {
+            _workerApp?.Dispose ();
+            _workerApp = null;
+
+            base.Dispose (disposing);
+        }
     }
 
     private class Staging
@@ -233,6 +250,7 @@ public class BackgroundWorkerCollection : Scenario
         {
             Staging = staging;
             _label.Text = "Work list:";
+            _listView.Enabled = true;
             _listView.SetSource (list);
             _start.Visible = false;
             Id = "";
@@ -258,7 +276,7 @@ public class BackgroundWorkerCollection : Scenario
             };
             Add (_label);
 
-            _listView = new ListView { X = 0, Y = 2, Width = Dim.Fill (), Height = Dim.Fill (2) };
+            _listView = new ListView { X = 0, Y = 2, Width = Dim.Fill (), Height = Dim.Fill (2), Enabled = false };
             Add (_listView);
 
             _start = new Button { Text = "Start", IsDefault = true, ClearOnVisibleFalse = false };
@@ -276,7 +294,7 @@ public class BackgroundWorkerCollection : Scenario
 
             KeyDown += (s, e) =>
                        {
-                           if (e.KeyCode == KeyCode.Esc)
+                           if (e == Application.QuitKey)
                            {
                                OnReportClosed (this, EventArgs.Empty);
                            }
@@ -299,7 +317,6 @@ public class BackgroundWorkerCollection : Scenario
 
         public Staging Staging { get; private set; }
         public event Action<StagingUIController> ReportClosed;
-        public void Run () { Application.Run (this); }
 
         private void OnReportClosed (object sender, EventArgs e)
         {
@@ -322,24 +339,45 @@ public class BackgroundWorkerCollection : Scenario
         public WorkerApp ()
         {
             Data = "WorkerApp";
+            Title = "Worker collection Log";
 
             Width = Dim.Percent (80);
             Height = Dim.Percent (50);
 
             ColorScheme = Colors.ColorSchemes ["Base"];
 
-            var label = new Label { X = Pos.Center (), Y = 0, Text = "Worker collection Log" };
-            Add (label);
-
             _listLog = new ListView
             {
                 X = 0,
-                Y = Pos.Bottom (label),
+                Y = 0,
                 Width = Dim.Fill (),
                 Height = Dim.Fill (),
                 Source = new ListWrapper (_log)
             };
             Add (_listLog);
+
+            // We don't want WorkerApp to respond to the quitkey
+            KeyBindings.Remove (Application.QuitKey);
+
+            Closing += WorkerApp_Closing;
+            Closed += WorkerApp_Closed;
+        }
+
+        private void WorkerApp_Closed (object sender, ToplevelEventArgs e)
+        {
+            CancelWorker ();
+        }
+        private void WorkerApp_Closing (object sender, ToplevelClosingEventArgs e)
+        {
+            Toplevel top = Application.OverlappedChildren.Find (x => x.Data.ToString () == "WorkerApp");
+
+            if (Visible && top == this)
+            {
+                Visible = false;
+                e.Cancel = true;
+
+                Application.OverlappedMoveNext ();
+            }
         }
 
         public void CancelWorker ()
@@ -403,15 +441,7 @@ public class BackgroundWorkerCollection : Scenario
                                              {
                                                  // Failed
                                                  WriteLog (
-                                                           $"Exception occurred {
-                                                               e.Error.Message
-                                                           } on Worker {
-                                                               staging.StartStaging
-                                                           }.{
-                                                               staging.StartStaging
-                                                               :fff} at {
-                                                               DateTime.Now
-                                                           }"
+                                                           $"Exception occurred {e.Error.Message} on Worker {staging.StartStaging}.{staging.StartStaging:fff} at {DateTime.Now}"
                                                           );
                                              }
                                              else if (e.Cancelled)
@@ -446,8 +476,14 @@ public class BackgroundWorkerCollection : Scenario
 
                                                  _stagingsUi.Add (stagingUI);
                                                  _stagingWorkers.Remove (staging);
-
-                                                 stagingUI.Run ();
+#if DEBUG_IDISPOSABLE
+                                                 if (Application.OverlappedTop is null)
+                                                 {
+                                                     stagingUI.Dispose ();
+                                                     return;
+                                                 }
+#endif
+                                                 Application.Run (stagingUI);
                                              }
                                          };
 
@@ -456,6 +492,7 @@ public class BackgroundWorkerCollection : Scenario
             if (stagingUI.Staging != null && stagingUI.Staging.StartStaging != null)
             {
                 staging = new Staging (stagingUI.Staging.StartStaging);
+                stagingUI.Dispose ();
                 WriteLog ($"Worker is started at {staging.StartStaging}.{staging.StartStaging:fff}");
 
                 if (_stagingWorkers == null)
@@ -465,6 +502,9 @@ public class BackgroundWorkerCollection : Scenario
 
                 _stagingWorkers.Add (staging, worker);
                 worker.RunWorkerAsync ();
+            }
+            else
+            {
                 stagingUI.Dispose ();
             }
         }
@@ -479,6 +519,7 @@ public class BackgroundWorkerCollection : Scenario
         {
             WriteLog ($"Report {obj.Staging.StartStaging}.{obj.Staging.StartStaging:fff} closed.");
             _stagingsUi.Remove (obj);
+            obj.Dispose ();
         }
     }
 }

+ 1 - 1
UICatalog/Scenarios/Buttons.cs

@@ -329,6 +329,6 @@ public class Buttons : Scenario
                                               }
                                           };
 
-        Application.Top.Ready += (s, e) => radioGroup.Refresh ();
+        Top.Ready += (s, e) => radioGroup.Refresh ();
     }
 }

+ 10 - 7
UICatalog/Scenarios/CharacterMap.cs

@@ -36,13 +36,14 @@ public class CharacterMap : Scenario
     public override void Init ()
     {
         Application.Init ();
-        Application.Top.ColorScheme = Colors.ColorSchemes ["Base"];
+        Top = new ();
+        Top.ColorScheme = Colors.ColorSchemes ["Base"];
     }
 
     public override void Setup ()
     {
         _charMap = new() { X = 0, Y = 1, Height = Dim.Fill () };
-        Application.Top.Add (_charMap);
+        Top.Add (_charMap);
 
         var jumpLabel = new Label
         {
@@ -51,19 +52,19 @@ public class CharacterMap : Scenario
             HotKeySpecifier = (Rune)'_',
             Text = "_Jump To Code Point:"
         };
-        Application.Top.Add (jumpLabel);
+        Top.Add (jumpLabel);
 
         var jumpEdit = new TextField
         {
             X = Pos.Right (jumpLabel) + 1, Y = Pos.Y (_charMap), Width = 10, Caption = "e.g. 01BE3"
         };
-        Application.Top.Add (jumpEdit);
+        Top.Add (jumpEdit);
 
         _errorLabel = new()
         {
             X = Pos.Right (jumpEdit) + 1, Y = Pos.Y (_charMap), ColorScheme = Colors.ColorSchemes ["error"], Text = "err"
         };
-        Application.Top.Add (_errorLabel);
+        Top.Add (_errorLabel);
 
 #if TEXT_CHANGED_TO_JUMP
         jumpEdit.TextChanged += JumpEdit_TextChanged;
@@ -135,7 +136,7 @@ public class CharacterMap : Scenario
                                                  _charMap.StartCodePoint = table.Data.ToArray () [args.NewRow].Start;
                                              };
 
-        Application.Top.Add (_categoryList);
+        Top.Add (_categoryList);
 
         _charMap.SelectedCodePoint = 0;
         _charMap.SetFocus ();
@@ -164,7 +165,7 @@ public class CharacterMap : Scenario
                     )
             ]
         };
-        Application.Top.Add (menu);
+        Top.Add (menu);
     }
 
     private void _categoryList_Initialized (object sender, EventArgs e) { _charMap.Width = Dim.Fill () - _categoryList.Width; }
@@ -898,6 +899,7 @@ internal class CharMap : ScrollView
                                    (s as Dialog)?.RequestStop ();
                                };
         Application.Run (waitIndicator);
+        waitIndicator.Dispose ();
 
         if (!string.IsNullOrEmpty (decResponse))
         {
@@ -1011,6 +1013,7 @@ internal class CharMap : ScrollView
             dlg.Add (json);
 
             Application.Run (dlg);
+            dlg.Dispose ();
         }
         else
         {

+ 21 - 21
UICatalog/Scenarios/ChineseUI.cs

@@ -6,20 +6,18 @@ namespace UICatalog.Scenarios;
 [ScenarioCategory ("Unicode")]
 public class ChineseUI : Scenario
 {
-    public override void Init ()
+    public override void Main ()
     {
         Application.Init ();
-        Toplevel top = Application.Top;
 
         var win = new Window
         {
-            Title = "Test",
+            Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}",
             X = 0,
             Y = 0,
             Width = Dim.Fill (),
             Height = Dim.Fill ()
         };
-        top.Add (win);
 
         var buttonPanel = new FrameView
         {
@@ -34,20 +32,20 @@ public class ChineseUI : Scenario
         var btn = new Button { X = 1, Y = 1, Text = "你" }; // v1: A
 
         btn.Accept += (s, e) =>
-                       {
-                           int result = MessageBox.Query (
-                                                          "Confirm",
-                                                          "Are you sure you want to quit ui?",
-                                                          0,
-                                                          "Yes",
-                                                          "No"
-                                                         );
-
-                           if (result == 0)
-                           {
-                               RequestStop ();
-                           }
-                       };
+                      {
+                          int result = MessageBox.Query (
+                                                         "Confirm",
+                                                         "Are you sure you want to quit ui?",
+                                                         0,
+                                                         "Yes",
+                                                         "No"
+                                                        );
+
+                          if (result == 0)
+                          {
+                              Application.RequestStop ();
+                          }
+                      };
 
         buttonPanel.Add (
                          btn,
@@ -55,8 +53,10 @@ public class ChineseUI : Scenario
                          new Button { X = 22, Y = 1, Text = "呀" } // v1: C
                         );
 
-        Application.Run ();
-    }
+        Application.Run (win);
+
+        win.Dispose ();
 
-    public override void Run () { }
+        Application.Shutdown ();
+    }
 }

+ 1 - 1
UICatalog/Scenarios/ClassExplorer.cs

@@ -55,7 +55,7 @@ public class ClassExplorer : Scenario
                                 )
             ]
         };
-        Application.Top.Add (menu);
+        Top.Add (menu);
 
         _treeView = new TreeView<object> { X = 0, Y = 1, Width = Dim.Percent (50), Height = Dim.Fill () };
 

+ 4 - 3
UICatalog/Scenarios/Clipping.cs

@@ -9,7 +9,8 @@ public class Clipping : Scenario
     public override void Init ()
     {
         Application.Init ();
-        Application.Top.ColorScheme = Colors.ColorSchemes ["Base"];
+        Top = new ();
+        Top.ColorScheme = Colors.ColorSchemes ["Base"];
     }
 
     public override void Setup ()
@@ -22,7 +23,7 @@ public class Clipping : Scenario
         {
             X = 0, Y = 0, Text = "ScrollView (new Rectangle (3, 3, 50, 20)) with a 200, 100 ContentSize..."
         };
-        Application.Top.Add (label);
+        Top.Add (label);
 
         var scrollView = new ScrollView { X = 3, Y = 3, Width = 50, Height = 20 };
         scrollView.ColorScheme = Colors.ColorSchemes ["Menu"];
@@ -79,6 +80,6 @@ public class Clipping : Scenario
 
         scrollView.Add (embedded1);
 
-        Application.Top.Add (scrollView);
+        Top.Add (scrollView);
     }
 }

+ 8 - 7
UICatalog/Scenarios/CollectionNavigatorTester.cs

@@ -80,7 +80,8 @@ public class CollectionNavigatorTester : Scenario
     public override void Init ()
     {
         Application.Init ();
-        Application.Top.ColorScheme = Colors.ColorSchemes ["Base"];
+        Top = new ();
+        Top.ColorScheme = Colors.ColorSchemes ["Base"];
     }
 
     public override void Setup ()
@@ -126,13 +127,13 @@ public class CollectionNavigatorTester : Scenario
             ]
         };
 
-        Application.Top.Add (menu);
+        Top.Add (menu);
 
         _items.Sort (StringComparer.OrdinalIgnoreCase);
 
         CreateListView ();
         var vsep = new LineView (Orientation.Vertical) { X = Pos.Right (_listView), Y = 1, Height = Dim.Fill () };
-        Application.Top.Add (vsep);
+        Top.Add (vsep);
         CreateTreeView ();
     }
 
@@ -148,7 +149,7 @@ public class CollectionNavigatorTester : Scenario
             Width = Dim.Percent (50),
             Height = 1
         };
-        Application.Top.Add (label);
+        Top.Add (label);
 
         _listView = new ListView
         {
@@ -159,7 +160,7 @@ public class CollectionNavigatorTester : Scenario
             AllowsMarking = false,
             AllowsMultipleSelection = false
         };
-        Application.Top.Add (_listView);
+        Top.Add (_listView);
 
         _listView.SetSource (_items);
 
@@ -178,14 +179,14 @@ public class CollectionNavigatorTester : Scenario
             Width = Dim.Percent (50),
             Height = 1
         };
-        Application.Top.Add (label);
+        Top.Add (label);
 
         _treeView = new TreeView
         {
             X = Pos.Right (_listView) + 1, Y = Pos.Bottom (label), Width = Dim.Fill (), Height = Dim.Fill ()
         };
         _treeView.Style.HighlightModelTextOnly = true;
-        Application.Top.Add (_treeView);
+        Top.Add (_treeView);
 
         var root = new TreeNode ("IsLetterOrDigit examples");
 

+ 3 - 2
UICatalog/Scenarios/CombiningMarks.cs

@@ -11,12 +11,13 @@ public class CombiningMarks : Scenario
         Application.Init ();
         ConfigurationManager.Themes.Theme = Theme;
         ConfigurationManager.Apply ();
-        Application.Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
+        Top = new ();
+        Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
     }
 
     public override void Setup ()
     {
-        Application.Top.DrawContentComplete += (s, e) =>
+        Top.DrawContentComplete += (s, e) =>
                                                {
                                                    Application.Driver.Move (0, 0);
                                                    Application.Driver.AddStr ("Terminal.Gui only supports combining marks that normalize. See Issue #2616.");

+ 36 - 35
UICatalog/Scenarios/ComputedLayout.cs

@@ -18,7 +18,8 @@ public class ComputedLayout : Scenario
         Application.Init ();
         ConfigurationManager.Themes.Theme = Theme;
         ConfigurationManager.Apply ();
-        Application.Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
+        Top = new ();
+        Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
     }
 
     public override void Setup ()
@@ -37,7 +38,7 @@ public class ComputedLayout : Scenario
             Text = rule
         };
 
-        Application.Top.Add (horizontalRuler);
+        Top.Add (horizontalRuler);
 
         // Demonstrate using Dim to create a vertical ruler that always measures the parent window's height
         const string vrule = "|\n1\n2\n3\n4\n5\n6\n7\n8\n9\n";
@@ -53,7 +54,7 @@ public class ComputedLayout : Scenario
             Text = vrule
         };
 
-        Application.Top.LayoutComplete += (s, a) =>
+        Top.LayoutComplete += (s, a) =>
                                           {
                                               horizontalRuler.Text =
                                                   rule.Repeat ((int)Math.Ceiling (horizontalRuler.Bounds.Width / (double)rule.Length)) [
@@ -64,15 +65,15 @@ public class ComputedLayout : Scenario
                                                       [..(verticalRuler.Bounds.Height * 2)];
                                           };
 
-        Application.Top.Add (verticalRuler);
+        Top.Add (verticalRuler);
 
         // Demonstrate At - Using Pos.At to locate a view in an absolute location
         var atButton = new Button { Text = "At(2,1)", X = Pos.At (2), Y = Pos.At (1) };
-        Application.Top.Add (atButton);
+        Top.Add (atButton);
 
         // Throw in a literal absolute - Should function identically to above
         var absoluteButton = new Button { Text = "X = 30, Y = 1", X = 30, Y = 1 };
-        Application.Top.Add (absoluteButton);
+        Top.Add (absoluteButton);
 
         // Demonstrate using Dim to create a window that fills the parent with a margin
         var margin = 10;
@@ -83,7 +84,7 @@ public class ComputedLayout : Scenario
                                   subWin.Title =
                                       $"{subWin.GetType ().Name} {{X={subWin.X},Y={subWin.Y},Width={subWin.Width},Height={subWin.Height}}}";
                               };
-        Application.Top.Add (subWin);
+        Top.Add (subWin);
 
         var i = 1;
         var txt = "Resize the terminal to see computed layout in action.";
@@ -208,7 +209,7 @@ public class ComputedLayout : Scenario
                        }
                       );
         frameView.Add (labelList.ToArray ());
-        Application.Top.Add (frameView);
+        Top.Add (frameView);
 
         frameView = new FrameView
         {
@@ -222,7 +223,7 @@ public class ComputedLayout : Scenario
                                      fv.Title =
                                          $"{frameView.GetType ().Name} {{X={fv.X},Y={fv.Y},Width={fv.Width},Height={fv.Height}}}";
                                  };
-        Application.Top.Add (frameView);
+        Top.Add (frameView);
 
         // Demonstrate Dim & Pos using percentages - a TextField that is 30% height and 80% wide
         var textView = new TextView
@@ -236,7 +237,7 @@ public class ComputedLayout : Scenario
 
         textView.Text =
             "This TextView should horizontally & vertically centered and \n10% of the screeen height, and 80% of its width.";
-        Application.Top.Add (textView);
+        Top.Add (textView);
 
         var oddballButton = new Button
         {
@@ -244,7 +245,7 @@ public class ComputedLayout : Scenario
             X = Pos.Center (),
             Y = Pos.Bottom (textView) + 1
         };
-        Application.Top.Add (oddballButton);
+        Top.Add (oddballButton);
 
         #region Issue2358
 
@@ -252,19 +253,19 @@ public class ComputedLayout : Scenario
         // Until https://github.com/gui-cs/Terminal.Gui/issues/2358 is fixed these won't work right
 
         oddballButton = new Button { Text = "Center + 0", X = Pos.Center () + 0, Y = Pos.Bottom (oddballButton) };
-        Application.Top.Add (oddballButton);
+        Top.Add (oddballButton);
 
         oddballButton = new Button { Text = "Center + 1", X = Pos.Center () + 1, Y = Pos.Bottom (oddballButton) };
-        Application.Top.Add (oddballButton);
+        Top.Add (oddballButton);
 
         oddballButton = new Button { Text = "0 + Center", X = 0 + Pos.Center (), Y = Pos.Bottom (oddballButton) };
-        Application.Top.Add (oddballButton);
+        Top.Add (oddballButton);
 
         oddballButton = new Button { Text = "1 + Center", X = 1 + Pos.Center (), Y = Pos.Bottom (oddballButton) };
-        Application.Top.Add (oddballButton);
+        Top.Add (oddballButton);
 
         oddballButton = new Button { Text = "Center - 1", X = Pos.Center () - 1, Y = Pos.Bottom (oddballButton) };
-        Application.Top.Add (oddballButton);
+        Top.Add (oddballButton);
 
         // This demonstrates nonsense: it the same as using Pos.AnchorEnd (100/2=50 + 100/2=50 = 100 - 50)
         // The `- Pos.Percent(5)` is there so at least something is visible
@@ -274,7 +275,7 @@ public class ComputedLayout : Scenario
             X = Pos.Center () + Pos.Center () - Pos.Percent (50),
             Y = Pos.Bottom (oddballButton)
         };
-        Application.Top.Add (oddballButton);
+        Top.Add (oddballButton);
 
         // This demonstrates nonsense: it the same as using Pos.AnchorEnd (100/2=50 + 100/2=50 = 100 - 50)
         // The `- Pos.Percent(5)` is there so at least something is visible
@@ -284,7 +285,7 @@ public class ComputedLayout : Scenario
             X = Pos.Percent (50) + Pos.Center () - Pos.Percent (50),
             Y = Pos.Bottom (oddballButton)
         };
-        Application.Top.Add (oddballButton);
+        Top.Add (oddballButton);
 
         // This demonstrates nonsense: it the same as using Pos.AnchorEnd (100/2=50 + 100/2=50 = 100 - 50)
         // The `- Pos.Percent(5)` is there so at least something is visible
@@ -294,7 +295,7 @@ public class ComputedLayout : Scenario
             X = Pos.Center () + Pos.Percent (50) - Pos.Percent (50),
             Y = Pos.Bottom (oddballButton)
         };
-        Application.Top.Add (oddballButton);
+        Top.Add (oddballButton);
 
         #endregion
 
@@ -305,14 +306,14 @@ public class ComputedLayout : Scenario
             X = Pos.Center () + Pos.Center () - Pos.Percent (50),
             Y = Pos.Bottom (oddballButton)
         };
-        Application.Top.Add (oddballButton);
+        Top.Add (oddballButton);
 
         // This demonstrates combining Percents)
         oddballButton = new Button
         {
             Text = "Percent(40) + Percent(10)", X = Pos.Percent (40) + Pos.Percent (10), Y = Pos.Bottom (oddballButton)
         };
-        Application.Top.Add (oddballButton);
+        Top.Add (oddballButton);
 
         // Demonstrate AnchorEnd - Button is anchored to bottom/right
         var anchorButton = new Button { Text = "Button using AnchorEnd", Y = Pos.AnchorEnd () - 1 };
@@ -322,12 +323,12 @@ public class ComputedLayout : Scenario
                                 {
                                     // This demonstrates how to have a dynamically sized button
                                     // Each time the button is clicked the button's text gets longer
-                                    // The call to Application.Top.LayoutSubviews causes the Computed layout to
+                                    // The call to Top.LayoutSubviews causes the Computed layout to
                                     // get updated. 
                                     anchorButton.Text += "!";
-                                    Application.Top.LayoutSubviews ();
+                                    Top.LayoutSubviews ();
                                 };
-        Application.Top.Add (anchorButton);
+        Top.Add (anchorButton);
 
         // Demonstrate AnchorEnd(n) 
         // This is intentionally convoluted to illustrate potential bugs.
@@ -341,7 +342,7 @@ public class ComputedLayout : Scenario
             X = 5,
             Y = Pos.AnchorEnd (2)
         };
-        Application.Top.Add (anchorEndLabel1);
+        Top.Add (anchorEndLabel1);
 
         // Demonstrate DimCombine (via AnchorEnd(n) - 1)
         // This is intentionally convoluted to illustrate potential bugs.
@@ -356,7 +357,7 @@ public class ComputedLayout : Scenario
             X = 5,
             Y = Pos.AnchorEnd (2) - 1 // Pos.Combine
         };
-        Application.Top.Add (anchorEndLabel2);
+        Top.Add (anchorEndLabel2);
 
         // Show positioning vertically using Pos.AnchorEnd via Pos.Combine
         var leftButton = new Button
@@ -368,10 +369,10 @@ public class ComputedLayout : Scenario
                               {
                                   // This demonstrates how to have a dynamically sized button
                                   // Each time the button is clicked the button's text gets longer
-                                  // The call to Application.Top.LayoutSubviews causes the Computed layout to
+                                  // The call to Top.LayoutSubviews causes the Computed layout to
                                   // get updated. 
                                   leftButton.Text += "!";
-                                  Application.Top.LayoutSubviews ();
+                                  Top.LayoutSubviews ();
                               };
 
         // show positioning vertically using Pos.AnchorEnd
@@ -384,10 +385,10 @@ public class ComputedLayout : Scenario
                                 {
                                     // This demonstrates how to have a dynamically sized button
                                     // Each time the button is clicked the button's text gets longer
-                                    // The call to Application.Top.LayoutSubviews causes the Computed layout to
+                                    // The call to Top.LayoutSubviews causes the Computed layout to
                                     // get updated. 
                                     centerButton.Text += "!";
-                                    Application.Top.LayoutSubviews ();
+                                    Top.LayoutSubviews ();
                                 };
 
         // show positioning vertically using another window and Pos.Bottom
@@ -397,18 +398,18 @@ public class ComputedLayout : Scenario
                                {
                                    // This demonstrates how to have a dynamically sized button
                                    // Each time the button is clicked the button's text gets longer
-                                   // The call to Application.Top.LayoutSubviews causes the Computed layout to
+                                   // The call to Top.LayoutSubviews causes the Computed layout to
                                    // get updated. 
                                    rightButton.Text += "!";
-                                   Application.Top.LayoutSubviews ();
+                                   Top.LayoutSubviews ();
                                };
 
         // Center three buttons with 5 spaces between them
         leftButton.X = Pos.Left (centerButton) - (Pos.Right (leftButton) - Pos.Left (leftButton)) - 5;
         rightButton.X = Pos.Right (centerButton) + 5;
 
-        Application.Top.Add (leftButton);
-        Application.Top.Add (centerButton);
-        Application.Top.Add (rightButton);
+        Top.Add (leftButton);
+        Top.Add (centerButton);
+        Top.Add (rightButton);
     }
 }

+ 17 - 19
UICatalog/Scenarios/ConfigurationEditor.cs

@@ -36,31 +36,17 @@ public class ConfigurationEditor : Scenario
         }
     }
 
-    // Don't create a Window, just return the top-level view
-    public override void Init ()
+    public override void Main ()
     {
         Application.Init ();
-        ConfigurationManager.Themes.Theme = Theme;
-        ConfigurationManager.Apply ();
-        Application.Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
-    }
-
-    public void Save ()
-    {
-        if (_tileView.MostFocused is ConfigTextView editor)
-        {
-            editor.Save ();
-        }
-    }
 
-    public override void Setup ()
-    {
+        Toplevel top = new ();
         _tileView = new TileView (0)
         {
             Width = Dim.Fill (), Height = Dim.Fill (1), Orientation = Orientation.Vertical, LineStyle = LineStyle.Single
         };
 
-        Application.Top.Add (_tileView);
+        top.Add (_tileView);
 
         _lenStatusItem = new StatusItem (KeyCode.CharMask, "Len: ", null);
 
@@ -78,9 +64,9 @@ public class ConfigurationEditor : Scenario
                                        }
                                       );
 
-        Application.Top.Add (statusBar);
+        top.Add (statusBar);
 
-        Application.Top.Loaded += (s, a) => Open ();
+        top.Loaded += (s, a) => Open ();
 
         _editorColorSchemeChanged += () =>
                                      {
@@ -94,6 +80,18 @@ public class ConfigurationEditor : Scenario
                                      };
 
         _editorColorSchemeChanged.Invoke ();
+
+        Application.Run (top);
+        top.Dispose ();
+
+        Application.Shutdown ();
+    }
+    public void Save ()
+    {
+        if (_tileView.MostFocused is ConfigTextView editor)
+        {
+            editor.Save ();
+        }
     }
 
     private void Open ()

+ 1 - 1
UICatalog/Scenarios/ContextMenus.cs

@@ -78,7 +78,7 @@ public class ContextMenus : Scenario
 
         Win.WantMousePositionReports = true;
 
-        Application.Top.Closed += (s, e) =>
+        Top.Closed += (s, e) =>
                                   {
                                       Thread.CurrentThread.CurrentUICulture = new CultureInfo ("en-US");
                                       Application.MouseEvent -= ApplicationMouseEvent;

+ 4 - 2
UICatalog/Scenarios/CsvEditor.cs

@@ -101,7 +101,7 @@ public class CsvEditor : Scenario
                                 )
             ]
         };
-        Application.Top.Add (menu);
+        Top.Add (menu);
 
         var statusBar = new StatusBar (
                                        new StatusItem []
@@ -123,7 +123,7 @@ public class CsvEditor : Scenario
                                                )
                                        }
                                       );
-        Application.Top.Add (statusBar);
+        Top.Add (statusBar);
 
         Win.Add (_tableView);
 
@@ -316,6 +316,7 @@ public class CsvEditor : Scenario
         tf.SetFocus ();
 
         Application.Run (d);
+        d.Dispose ();
 
         enteredText = okPressed ? tf.Text : null;
 
@@ -455,6 +456,7 @@ public class CsvEditor : Scenario
         {
             Open (ofd.Path);
         }
+        ofd.Dispose ();
     }
 
     private void Open (string filename)

+ 2 - 1
UICatalog/Scenarios/Dialogs.cs

@@ -155,7 +155,7 @@ public class Dialogs : Scenario
                 + frame.GetAdornmentsThickness ().Vertical;
         }
 
-        Application.Top.LayoutComplete += Top_LayoutComplete;
+        Top.LayoutComplete += Top_LayoutComplete;
 
         Win.Add (frame);
 
@@ -192,6 +192,7 @@ public class Dialogs : Scenario
                                                                        buttonPressedLabel
                                                                       );
                                         Application.Run (dlg);
+                                        dlg.Dispose ();
                                     };
 
         Win.Add (showDialogButton);

+ 13 - 10
UICatalog/Scenarios/DynamicMenuBar.cs

@@ -18,9 +18,11 @@ public class DynamicMenuBar : Scenario
     {
         Application.Init ();
 
-        Application.Top.Add (
-                             new DynamicMenuBarSample { Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}" }
-                            );
+        Top = new ();
+
+        Top.Add (
+                 new DynamicMenuBarSample { Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}" }
+                );
     }
 
     public class Binding
@@ -417,9 +419,9 @@ public class DynamicMenuBar : Scenario
                 EditMenuBarItem (_menuItem);
             }
 
-            var _btnOk = new Button { IsDefault = true, Text = "Ok" };
+            var btnOk = new Button { IsDefault = true, Text = "Ok" };
 
-            _btnOk.Accept += (s, e) =>
+            btnOk.Accept += (s, e) =>
                               {
                                   if (string.IsNullOrEmpty (TextTitle.Text))
                                   {
@@ -431,21 +433,22 @@ public class DynamicMenuBar : Scenario
                                       Application.RequestStop ();
                                   }
                               };
-            var _btnCancel = new Button { Text = "Cancel" };
+            var btnCancel = new Button { Text = "Cancel" };
 
-            _btnCancel.Accept += (s, e) =>
+            btnCancel.Accept += (s, e) =>
                                   {
                                       TextTitle.Text = string.Empty;
                                       Application.RequestStop ();
                                   };
-            var _dialog = new Dialog { Title = "Enter the menu details.", Buttons = [_btnOk, _btnCancel] };
+            var dialog = new Dialog { Title = "Enter the menu details.", Buttons = [btnOk, btnCancel] };
 
             Width = Dim.Fill ();
             Height = Dim.Fill () - 1;
-            _dialog.Add (this);
+            dialog.Add (this);
             TextTitle.SetFocus ();
             TextTitle.CursorPosition = TextTitle.Text.Length;
-            Application.Run (_dialog);
+            Application.Run (dialog);
+            dialog.Dispose ();
 
             if (valid)
             {

+ 13 - 10
UICatalog/Scenarios/DynamicStatusBar.cs

@@ -17,9 +17,11 @@ public class DynamicStatusBar : Scenario
     {
         Application.Init ();
 
-        Application.Top.Add (
-                             new DynamicStatusBarSample { Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}" }
-                            );
+        Top = new ();
+
+        Top.Add (
+                 new DynamicStatusBarSample { Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}" }
+                );
     }
 
     public class Binding
@@ -253,9 +255,9 @@ public class DynamicStatusBar : Scenario
                 EditStatusItem (_statusItem);
             }
 
-            var _btnOk = new Button { IsDefault = true, Text = "OK" };
+            var btnOk = new Button { IsDefault = true, Text = "OK" };
 
-            _btnOk.Accept += (s, e) =>
+            btnOk.Accept += (s, e) =>
                               {
                                   if (string.IsNullOrEmpty (TextTitle.Text))
                                   {
@@ -275,21 +277,22 @@ public class DynamicStatusBar : Scenario
                                       Application.RequestStop ();
                                   }
                               };
-            var _btnCancel = new Button { Text = "Cancel" };
+            var btnCancel = new Button { Text = "Cancel" };
 
-            _btnCancel.Accept += (s, e) =>
+            btnCancel.Accept += (s, e) =>
                                   {
                                       TextTitle.Text = string.Empty;
                                       Application.RequestStop ();
                                   };
-            var _dialog = new Dialog { Title = "Enter the menu details.", Buttons = [_btnOk, _btnCancel] };
+            var dialog = new Dialog { Title = "Enter the menu details.", Buttons = [btnOk, btnCancel] };
 
             Width = Dim.Fill ();
             Height = Dim.Fill () - 1;
-            _dialog.Add (this);
+            dialog.Add (this);
             TextTitle.SetFocus ();
             TextTitle.CursorPosition = TextTitle.Text.Length;
-            Application.Run (_dialog);
+            Application.Run (dialog);
+            dialog.Dispose ();
 
             return valid
                        ? new DynamicStatusItem

+ 16 - 9
UICatalog/Scenarios/Editor.cs

@@ -42,6 +42,8 @@ public class Editor : Scenario
         ConfigurationManager.Themes.Theme = Theme;
         ConfigurationManager.Apply ();
 
+        Top = new ();
+
         Win = new Window
         {
             Title = _fileName ?? "Untitled",
@@ -51,7 +53,7 @@ public class Editor : Scenario
             Height = Dim.Fill (),
             ColorScheme = Colors.ColorSchemes [TopLevelColorScheme]
         };
-        Application.Top.Add (Win);
+        Top.Add (Win);
 
         _textView = new TextView
         {
@@ -238,7 +240,7 @@ public class Editor : Scenario
             ]
         };
 
-        Application.Top.Add (menu);
+        Top.Add (menu);
 
         var siCursorPosition = new StatusItem (KeyCode.Null, "", null);
 
@@ -268,7 +270,7 @@ public class Editor : Scenario
                                                  statusBar.SetNeedsDisplay ();
                                              };
 
-        Application.Top.Add (statusBar);
+        Top.Add (statusBar);
 
         _scrollBar = new ScrollBarView (_textView, true);
 
@@ -374,7 +376,7 @@ public class Editor : Scenario
                            }
                        };
 
-        Application.Top.Closed += (s, e) => Thread.CurrentThread.CurrentUICulture = new CultureInfo ("en-US");
+        Top.Closed += (s, e) => Thread.CurrentThread.CurrentUICulture = new CultureInfo ("en-US");
     }
 
     public override void Setup () { }
@@ -1061,6 +1063,7 @@ public class Editor : Scenario
             _fileName = d.FilePaths [0];
             LoadFile ();
         }
+        d.Dispose ();
     }
 
     private void Paste ()
@@ -1259,12 +1262,16 @@ public class Editor : Scenario
         };
         var sd = new SaveDialog { Title = "Save file", AllowedTypes = aTypes };
 
-        sd.Path = Path.Combine (sd.FileName, Win.Title);
+        sd.Path = Win.Title;
         Application.Run (sd);
+        bool canceled = sd.Canceled;
+        string path = sd.Path;
+        string fileName = sd.FileName;
+        sd.Dispose ();
 
-        if (!sd.Canceled)
+        if (!canceled)
         {
-            if (File.Exists (sd.Path))
+            if (File.Exists (path))
             {
                 if (MessageBox.Query (
                                       "Save File",
@@ -1274,7 +1281,7 @@ public class Editor : Scenario
                                      )
                     == 1)
                 {
-                    return SaveFile (sd.FileName, sd.Path);
+                    return SaveFile (fileName, path);
                 }
 
                 _saved = false;
@@ -1282,7 +1289,7 @@ public class Editor : Scenario
                 return _saved;
             }
 
-            return SaveFile (sd.FileName, sd.Path);
+            return SaveFile (fileName, path);
         }
 
         _saved = false;

+ 10 - 3
UICatalog/Scenarios/FileDialogExamples.cs

@@ -204,7 +204,14 @@ public class FileDialogExamples : Scenario
 
         Application.Run (fd);
 
-        if (fd.Canceled)
+        var canceled = fd.Canceled;
+        var multiSelected = fd.MultiSelected;
+        var path = fd.Path;
+
+        // This needs to be disposed before opening other toplevel
+        fd.Dispose ();
+
+        if (canceled)
         {
             MessageBox.Query (
                               "Canceled",
@@ -216,7 +223,7 @@ public class FileDialogExamples : Scenario
         {
             MessageBox.Query (
                               "Chosen!",
-                              "You chose:" + Environment.NewLine + string.Join (Environment.NewLine, fd.MultiSelected.Select (m => m)),
+                              "You chose:" + Environment.NewLine + string.Join (Environment.NewLine, multiSelected.Select (m => m)),
                               "Ok"
                              );
         }
@@ -224,7 +231,7 @@ public class FileDialogExamples : Scenario
         {
             MessageBox.Query (
                               "Chosen!",
-                              "You chose:" + Environment.NewLine + fd.Path,
+                              "You chose:" + Environment.NewLine + path,
                               "Ok"
                              );
         }

+ 17 - 25
UICatalog/Scenarios/Generic.cs

@@ -4,36 +4,28 @@ namespace UICatalog.Scenarios;
 
 [ScenarioMetadata ("Generic", "Generic sample - A template for creating new Scenarios")]
 [ScenarioCategory ("Controls")]
-public class MyScenario : Scenario
+public sealed class MyScenario : Scenario
 {
-    public override void Init ()
+    public override void Main ()
     {
-        // The base `Scenario.Init` implementation:
-        //  - Calls `Application.Init ()`
-        //  - Adds a full-screen Window to Application.Top with a title
-        //    that reads "Press <hotkey> to Quit". Access this Window with `this.Win`.
-        //  - Sets the Theme & the ColorScheme property of `this.Win` to `colorScheme`.
-        // To override this, implement an override of `Init`.
-
-        //base.Init ();
-
-        // A common, alternate, implementation where `this.Win` is not used is below. This code
-        // leverages ConfigurationManager to borrow the color scheme settings from UICatalog:
-
+        // Init
         Application.Init ();
-        ConfigurationManager.Themes.Theme = Theme;
-        ConfigurationManager.Apply ();
-        Application.Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
-    }
 
-    public override void Setup ()
-    {
-        // Put scenario code here (in a real app, this would be the code
-        // that would setup the app before `Application.Run` is called`).
-        // With a Scenario, after UI Catalog calls `Scenario.Setup` it calls
-        // `Scenario.Run` which calls `Application.Run`. Example:
+        // Setup - Create a top-level application window and configure it.
+        Window appWindow = new ()
+        {
+            Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}"
+        };
 
         var button = new Button { X = Pos.Center (), Y = Pos.Center (), Text = "Press me!" };
-        Application.Top.Add (button);
+        button.Accept += (s, e) => MessageBox.ErrorQuery ("Error", "You pressed the button!", "Ok");
+        appWindow.Add (button);
+
+        // Run - Start the application.
+        Application.Run (appWindow);
+        appWindow.Dispose ();
+
+        // Shutdown - Calling Application.Shutdown is required.
+        Application.Shutdown ();
     }
 }

+ 2 - 2
UICatalog/Scenarios/GraphViewExample.cs

@@ -133,7 +133,7 @@ public class GraphViewExample : Scenario
                                 )
             ]
         };
-        Application.Top.Add (menu);
+        Top.Add (menu);
 
         _graphView = new GraphView
         {
@@ -179,7 +179,7 @@ public class GraphViewExample : Scenario
                                                )
                                        }
                                       );
-        Application.Top.Add (statusBar);
+        Top.Add (statusBar);
     }
 
     private void EnableDiagnostics ()

+ 3 - 2
UICatalog/Scenarios/HexEditor.cs

@@ -74,7 +74,7 @@ public class HexEditor : Scenario
                                 )
             ]
         };
-        Application.Top.Add (menu);
+        Top.Add (menu);
 
         _statusBar = new StatusBar (
                                     new []
@@ -101,7 +101,7 @@ public class HexEditor : Scenario
                                                                             )
                                     }
                                    );
-        Application.Top.Add (_statusBar);
+        Top.Add (_statusBar);
     }
 
     private void _hexView_Edited (object sender, HexViewEditEventArgs e) { _saved = false; }
@@ -203,6 +203,7 @@ public class HexEditor : Scenario
             _hexView.Source = LoadFile ();
             _hexView.DisplayStart = 0;
         }
+        d.Dispose ();
     }
 
     private void Paste () { MessageBox.ErrorQuery ("Not Implemented", "Functionality not yet implemented.", "Ok"); }

+ 24 - 23
UICatalog/Scenarios/HotKeys.cs

@@ -12,21 +12,22 @@ public class HotKeys : Scenario
         Application.Init ();
         ConfigurationManager.Themes.Theme = Theme;
         ConfigurationManager.Apply ();
-        Application.Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
-        Application.Top.BorderStyle = LineStyle.RoundedDotted;
-        Application.Top.Title = $"{Application.QuitKey} to _Quit - Scenario: {GetName ()}";
+        Top = new ();
+        Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
+        Top.BorderStyle = LineStyle.RoundedDotted;
+        Top.Title = $"{Application.QuitKey} to _Quit - Scenario: {GetName ()}";
     }
 
     public override void Run ()
     {
         var textViewLabel = new Label { Text = "_TextView:", X = 0, Y = 0 };
-        Application.Top.Add (textViewLabel);
+        Top.Add (textViewLabel);
         
         var textField = new TextField (){ X = Pos.Right (textViewLabel) + 1, Y = 0, Width = 10 };
-        Application.Top.Add (textField);
+        Top.Add (textField);
 
         var viewLabel = new Label { Text = "_View:", X = 0, Y = Pos.Bottom (textField) + 1 };
-        Application.Top.Add (viewLabel);
+        Top.Add (viewLabel);
 
         var view = new View () { 
             Title = "View (_focusable)", 
@@ -35,10 +36,10 @@ public class HotKeys : Scenario
             X = Pos.Right (viewLabel) + 1, Y = Pos.Top (viewLabel), Width = 30, Height = 3,
             BorderStyle = LineStyle.Dashed,
         };
-        Application.Top.Add (view);
+        Top.Add (view);
 
         viewLabel = new Label { Text = "Vi_ew:", X = 0, Y = Pos.Bottom (view) + 1 };
-        Application.Top.Add (viewLabel);
+        Top.Add (viewLabel);
 
         view = new View ()
         {
@@ -47,10 +48,10 @@ public class HotKeys : Scenario
             X = Pos.Right (viewLabel) + 1, Y = Pos.Top (viewLabel), Width = 30, Height = 3,
             BorderStyle = LineStyle.Dashed,
         };
-        Application.Top.Add (view);
+        Top.Add (view);
 
         var labelWithFrameLabel = new Label { Text = "_Label with Frame:", X = 0, Y = Pos.Bottom (view) + 1 };
-        Application.Top.Add (labelWithFrameLabel);
+        Top.Add (labelWithFrameLabel);
 
         var labelWithFrameFocusable = new Label ()
         {
@@ -60,10 +61,10 @@ public class HotKeys : Scenario
             X = Pos.Right (labelWithFrameLabel) + 1, Y = Pos.Top (labelWithFrameLabel), Width = 40, Height = 3,
             BorderStyle = LineStyle.Dashed,
         };
-        Application.Top.Add (labelWithFrameFocusable);
+        Top.Add (labelWithFrameFocusable);
 
         labelWithFrameLabel = new Label { Text = "L_abel with Frame:", X = 0, Y = Pos.Bottom (labelWithFrameFocusable) + 1 };
-        Application.Top.Add (labelWithFrameLabel);
+        Top.Add (labelWithFrameLabel);
 
         var labelWithFrame = new Label ()
         {
@@ -72,11 +73,11 @@ public class HotKeys : Scenario
             X = Pos.Right (labelWithFrameLabel) + 1, Y = Pos.Top (labelWithFrameLabel), Width = 40, Height = 3,
             BorderStyle = LineStyle.Dashed,
         };
-        Application.Top.Add (labelWithFrame);
+        Top.Add (labelWithFrame);
 
         
         var buttonWithFrameLabel = new Label { Text = "_Button with Frame:", X = 0, Y = Pos.Bottom (labelWithFrame) + 1 };
-        Application.Top.Add (buttonWithFrameLabel);
+        Top.Add (buttonWithFrameLabel);
 
         var buttonWithFrameFocusable = new Button ()
         {
@@ -86,10 +87,10 @@ public class HotKeys : Scenario
             X = Pos.Right (buttonWithFrameLabel) + 1, Y = Pos.Top (buttonWithFrameLabel), Width = 40, Height = 3,
             BorderStyle = LineStyle.Dashed,
         };
-        Application.Top.Add (buttonWithFrameFocusable);
+        Top.Add (buttonWithFrameFocusable);
 
         buttonWithFrameLabel = new Label { Text = "Butt_on with Frame:", X = 0, Y = Pos.Bottom (buttonWithFrameFocusable) + 1 };
-        Application.Top.Add (buttonWithFrameLabel);
+        Top.Add (buttonWithFrameLabel);
 
         var buttonWithFrame = new Button ()
         {
@@ -99,12 +100,12 @@ public class HotKeys : Scenario
             CanFocus = false,
             BorderStyle = LineStyle.Dashed,
         };
-        Application.Top.Add (buttonWithFrame);
+        Top.Add (buttonWithFrame);
 
 
 
         var checkboxWithFrameLabel = new Label { Text = "_Checkbox with Frame:", X = 0, Y = Pos.Bottom (buttonWithFrame) + 1 };
-        Application.Top.Add (checkboxWithFrameLabel);
+        Top.Add (checkboxWithFrameLabel);
 
         var checkboxWithFrameFocusable = new CheckBox
         {
@@ -114,10 +115,10 @@ public class HotKeys : Scenario
             X = Pos.Right (checkboxWithFrameLabel) + 1, Y = Pos.Top (checkboxWithFrameLabel), Width = 40, Height = 3,
             BorderStyle = LineStyle.Dashed,
         };
-        Application.Top.Add (checkboxWithFrameFocusable);
+        Top.Add (checkboxWithFrameFocusable);
 
         checkboxWithFrameLabel = new Label { Text = "Checkb_ox with Frame:", X = 0, Y = Pos.Bottom (checkboxWithFrameFocusable) + 1 };
-        Application.Top.Add (checkboxWithFrameLabel);
+        Top.Add (checkboxWithFrameLabel);
 
         var checkboxWithFrame = new CheckBox
         {
@@ -127,12 +128,12 @@ public class HotKeys : Scenario
             CanFocus = false,
             BorderStyle = LineStyle.Dashed,
         };
-        Application.Top.Add (checkboxWithFrame);
+        Top.Add (checkboxWithFrame);
 
 
         var button = new Button { X = Pos.Center (), Y = Pos.AnchorEnd (1), Text = "_Press me!" };
-        Application.Top.Add (button);
+        Top.Add (button);
 
-        Application.Run (Application.Top);
+        Application.Run (Top);
     }
 }

+ 3 - 0
UICatalog/Scenarios/Images.cs

@@ -66,11 +66,14 @@ public class Images : Scenario
 
                                     if (ofd.Canceled)
                                     {
+                                        ofd.Dispose ();
                                         return;
                                     }
 
                                     string path = ofd.FilePaths [0];
 
+                                    ofd.Dispose ();
+
                                     if (string.IsNullOrWhiteSpace (path))
                                     {
                                         return;

+ 3 - 2
UICatalog/Scenarios/InteractiveTree.cs

@@ -23,7 +23,7 @@ public class InteractiveTree : Scenario
                 new MenuBarItem ("_File", new MenuItem [] { new ("_Quit", "", Quit) })
             ]
         };
-        Application.Top.Add (menu);
+        Top.Add (menu);
 
         _treeView = new TreeView { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill (1) };
         _treeView.KeyDown += TreeView_KeyPress;
@@ -55,7 +55,7 @@ public class InteractiveTree : Scenario
                                                )
                                        }
                                       );
-        Application.Top.Add (statusBar);
+        Top.Add (statusBar);
     }
 
     private void AddChildNode ()
@@ -103,6 +103,7 @@ public class InteractiveTree : Scenario
         tf.SetFocus ();
 
         Application.Run (d);
+        d.Dispose ();
 
         enteredText = okPressed ? tf.Text : null;
 

+ 7 - 3
UICatalog/Scenarios/LineCanvasExperiment.cs

@@ -8,7 +8,11 @@ namespace UICatalog.Scenarios;
 [ScenarioCategory ("Proof of Concept")]
 public class LineCanvasExperiment : Scenario
 {
-    public override void Init () { Application.Init (); }
+    public override void Init ()
+    {
+        Application.Init ();
+        Top = new ();
+    }
 
     /// <summary>Setup the scenario.</summary>
     public override void Setup ()
@@ -18,7 +22,7 @@ public class LineCanvasExperiment : Scenario
         //	new MenuItem ("_Quit", "", () => Application.RequestStop()),
         //}) });
 
-        //Application.Top.Add (menu);
+        //Top.Add (menu);
 
         var frame1 = new FrameView
         {
@@ -33,7 +37,7 @@ public class LineCanvasExperiment : Scenario
 
         //View.Diagnostics ^= DiagnosticFlags.FrameRuler;
 
-        Application.Top.Add (frame1);
+        Top.Add (frame1);
 
         var win1 = new Window
         {

+ 2 - 2
UICatalog/Scenarios/LineViewExample.cs

@@ -22,7 +22,7 @@ public class LineViewExample : Scenario
                 new MenuBarItem ("_File", new MenuItem [] { new ("_Quit", "", () => Quit ()) })
             ]
         };
-        Application.Top.Add (menu);
+        Top.Add (menu);
 
         Win.Add (new Label { Y = 0, Text = "Regular Line" });
 
@@ -82,7 +82,7 @@ public class LineViewExample : Scenario
                                                )
                                        }
                                       );
-        Application.Top.Add (statusBar);
+        Top.Add (statusBar);
     }
 
     private void Quit () { Application.RequestStop (); }

+ 3 - 2
UICatalog/Scenarios/ListColumns.cs

@@ -208,7 +208,7 @@ public class ListColumns : Scenario
             ]
         };
 
-        Application.Top.Add (menu);
+        Top.Add (menu);
 
         var statusBar = new StatusBar (
                                        new StatusItem []
@@ -235,7 +235,7 @@ public class ListColumns : Scenario
                                                )
                                        }
                                       );
-        Application.Top.Add (statusBar);
+        Top.Add (statusBar);
 
         Win.Add (_listColView);
 
@@ -294,6 +294,7 @@ public class ListColumns : Scenario
         tf.SetFocus ();
 
         Application.Run (d);
+        d.Dispose ();
 
         if (accepted)
         {

+ 3 - 1
UICatalog/Scenarios/Localization.cs

@@ -93,7 +93,7 @@ public class Localization : Scenario
                                 )
             ]
         };
-        Application.Top.Add (menu);
+        Top.Add (menu);
 
         var selectLanguageLabel = new Label
         {
@@ -200,6 +200,7 @@ public class Localization : Scenario
         }
 
         Application.Run (dialog);
+        dialog.Dispose ();
     }
 
     public void ShowWizard ()
@@ -209,6 +210,7 @@ public class Localization : Scenario
         wizard.AddStep (new WizardStep { HelpText = "Wizard step 2", NextButtonText = ">>> (_N)" });
         wizard.AddStep (new WizardStep { HelpText = "Wizard last step" });
         Application.Run (wizard);
+        wizard.Dispose ();
     }
 
     private void LanguageComboBox_SelectChanged (object sender, ListViewItemEventArgs e)

+ 17 - 16
UICatalog/Scenarios/MenuBarScenario.cs

@@ -199,7 +199,8 @@ public class MenuBarScenario : Scenario
     public override void Init ()
     {
         Application.Init ();
-        Application.Top.ColorScheme = Colors.ColorSchemes ["Base"];
+        Top = new ();
+        Top.ColorScheme = Colors.ColorSchemes ["Base"];
     }
 
     public override void Setup ()
@@ -208,34 +209,34 @@ public class MenuBarScenario : Scenario
         MenuItem miCurrent = null;
 
         var label = new Label { X = 0, Y = 10, Text = "Last Key: " };
-        Application.Top.Add (label);
+        Top.Add (label);
 
         _lastKey = new Label { X = Pos.Right (label), Y = Pos.Top (label), Text = "" };
 
-        Application.Top.Add (_lastKey);
+        Top.Add (_lastKey);
         label = new Label { X = 0, Y = Pos.Bottom (label), Text = "Current MenuBarItem: " };
-        Application.Top.Add (label);
+        Top.Add (label);
 
         _currentMenuBarItem = new Label { X = Pos.Right (label), Y = Pos.Top (label), Text = "" };
-        Application.Top.Add (_currentMenuBarItem);
+        Top.Add (_currentMenuBarItem);
 
         label = new Label { X = 0, Y = Pos.Bottom (label), Text = "Current MenuItem: " };
-        Application.Top.Add (label);
+        Top.Add (label);
 
         _currentMenuItem = new Label { X = Pos.Right (label), Y = Pos.Top (label), Text = "" };
-        Application.Top.Add (_currentMenuItem);
+        Top.Add (_currentMenuItem);
 
         label = new Label { X = 0, Y = Pos.Bottom (label), Text = "Last Action: " };
-        Application.Top.Add (label);
+        Top.Add (label);
 
         _lastAction = new Label { X = Pos.Right (label), Y = Pos.Top (label), Text = "" };
-        Application.Top.Add (_lastAction);
+        Top.Add (_lastAction);
 
         label = new Label { X = 0, Y = Pos.Bottom (label), Text = "Focused View: " };
-        Application.Top.Add (label);
+        Top.Add (label);
 
         _focusedView = new Label { X = Pos.Right (label), Y = Pos.Top (label), Text = "" };
-        Application.Top.Add (_focusedView);
+        Top.Add (_focusedView);
 
         MenuBar menuBar = CreateTestMenu (
                                           s =>
@@ -276,21 +277,21 @@ public class MenuBarScenario : Scenario
                                };
 
         // There's no focus change event, so this is a bit of a hack.
-        menuBar.LayoutComplete += (s, e) => { _focusedView.Text = Application.Top.MostFocused?.ToString () ?? "None"; };
+        menuBar.LayoutComplete += (s, e) => { _focusedView.Text = Top.MostFocused?.ToString () ?? "None"; };
 
         var openBtn = new Button { X = Pos.Center (), Y = 4, Text = "_Open Menu", IsDefault = true };
         openBtn.Accept += (s, e) => { menuBar.OpenMenu (); };
-        Application.Top.Add (openBtn);
+        Top.Add (openBtn);
 
         var hideBtn = new Button { X = Pos.Center (), Y = Pos.Bottom (openBtn), Text = "Toggle Menu._Visible" };
         hideBtn.Accept += (s, e) => { menuBar.Visible = !menuBar.Visible; };
-        Application.Top.Add (hideBtn);
+        Top.Add (hideBtn);
 
         var enableBtn = new Button { X = Pos.Center (), Y = Pos.Bottom (hideBtn), Text = "_Toggle Menu.Enable" };
         enableBtn.Accept += (s, e) => { menuBar.Enabled = !menuBar.Enabled; };
-        Application.Top.Add (enableBtn);
+        Top.Add (enableBtn);
 
-        Application.Top.Add (menuBar);
+        Top.Add (menuBar);
     }
 
     private void SetCurrentMenuBarItem (MenuItem mbi) { _currentMenuBarItem.Text = mbi != null ? mbi.Title : "Closed"; }

+ 2 - 2
UICatalog/Scenarios/MessageBoxes.cs

@@ -188,10 +188,10 @@ public class MessageBoxes : Scenario
                 + styleRadioGroup.Frame.Height
                 + ckbWrapMessage.Frame.Height
                 + frame.GetAdornmentsThickness ().Vertical;
-            Application.Top.Loaded -= Top_LayoutComplete;
+            Top.Loaded -= Top_LayoutComplete;
         }
 
-        Application.Top.LayoutComplete += Top_LayoutComplete;
+        Top.LayoutComplete += Top_LayoutComplete;
 
         label = new Label
         {

+ 3 - 2
UICatalog/Scenarios/MultiColouredTable.cs

@@ -29,7 +29,7 @@ public class MultiColouredTable : Scenario
                 new MenuBarItem ("_File", new MenuItem [] { new ("_Quit", "", () => Quit ()) })
             ]
         };
-        Application.Top.Add (menu);
+        Top.Add (menu);
 
         var statusBar = new StatusBar (
                                        new StatusItem []
@@ -41,7 +41,7 @@ public class MultiColouredTable : Scenario
                                                )
                                        }
                                       );
-        Application.Top.Add (statusBar);
+        Top.Add (statusBar);
 
         Win.Add (_tableView);
 
@@ -117,6 +117,7 @@ public class MultiColouredTable : Scenario
         tf.SetFocus ();
 
         Application.Run (d);
+        d.Dispose ();
 
         enteredText = okPressed ? tf.Text : null;
 

+ 120 - 108
UICatalog/Scenarios/Notepad.cs

@@ -12,14 +12,92 @@ public class Notepad : Scenario
 {
     private TabView _focusedTabView;
     private StatusItem _lenStatusItem;
-    private int _numbeOfNewTabs = 1;
+    private int _numNewTabs = 1;
     private TabView _tabView;
 
-    // Don't create a Window, just return the top-level view
-    public override void Init ()
+    public override void Main ()
     {
         Application.Init ();
-        Application.Top.ColorScheme = Colors.ColorSchemes ["Base"];
+
+        Toplevel top = new ();
+
+        var menu = new MenuBar
+        {
+            Menus =
+            [
+                new (
+                     "_File",
+                     new MenuItem []
+                     {
+                         new (
+                              "_New",
+                              "",
+                              () => New (),
+                              null,
+                              null,
+                              KeyCode.N
+                              | KeyCode.CtrlMask
+                              | KeyCode.AltMask
+                             ),
+                         new ("_Open", "", () => Open ()),
+                         new ("_Save", "", () => Save ()),
+                         new ("Save _As", "", () => SaveAs ()),
+                         new ("_Close", "", () => Close ()),
+                         new ("_Quit", "", () => Quit ())
+                     }
+                    ),
+                new (
+                     "_About",
+                     "",
+                     () => MessageBox.Query ("Notepad", "About Notepad...", "Ok")
+                    )
+            ]
+        };
+        top.Add (menu);
+
+        _tabView = CreateNewTabView ();
+
+        _tabView.Style.ShowBorder = true;
+        _tabView.ApplyStyleChanges ();
+
+        // Start with only a single view but support splitting to show side by side
+        var split = new TileView (1) { X = 0, Y = 1, Width = Dim.Fill (), Height = Dim.Fill (1) };
+        split.Tiles.ElementAt (0).ContentView.Add (_tabView);
+        split.LineStyle = LineStyle.None;
+
+        top.Add (split);
+
+        _lenStatusItem = new (KeyCode.CharMask, "Len: ", null);
+
+        var statusBar = new StatusBar (
+                                       new []
+                                       {
+                                           new (
+                                                Application.QuitKey,
+                                                $"{Application.QuitKey} to Quit",
+                                                () => Quit ()
+                                               ),
+
+                                           // These shortcut keys don't seem to work correctly in linux 
+                                           //new StatusItem(Key.CtrlMask | Key.N, "~^O~ Open", () => Open()),
+                                           //new StatusItem(Key.CtrlMask | Key.N, "~^N~ New", () => New()),
+
+                                           new (KeyCode.CtrlMask | KeyCode.S, "~^S~ Save", () => Save ()),
+                                           new (KeyCode.CtrlMask | KeyCode.W, "~^W~ Close", () => Close ()),
+                                           _lenStatusItem
+                                       }
+                                      );
+        _focusedTabView = _tabView;
+        _tabView.SelectedTabChanged += TabView_SelectedTabChanged;
+        _tabView.Enter += (s, e) => _focusedTabView = _tabView;
+
+        top.Add (statusBar);
+        top.Ready += (s, e) => New ();
+
+        Application.Run (top);
+        top.Dispose ();
+
+        Application.Shutdown ();
     }
 
     public void Save () { Save (_focusedTabView, _focusedTabView.SelectedTab); }
@@ -56,95 +134,25 @@ public class Notepad : Scenario
 
         if (string.IsNullOrWhiteSpace (fd.Path))
         {
+            fd.Dispose ();
+
             return false;
         }
 
         if (fd.Canceled)
         {
+            fd.Dispose ();
+
             return false;
         }
 
-        tab.File = new FileInfo (fd.Path);
+        tab.File = new (fd.Path);
         tab.Text = fd.FileName;
         tab.Save ();
 
-        return true;
-    }
+        fd.Dispose ();
 
-    public override void Setup ()
-    {
-        var menu = new MenuBar
-        {
-            Menus =
-            [
-                new MenuBarItem (
-                                 "_File",
-                                 new MenuItem []
-                                 {
-                                     new (
-                                          "_New",
-                                          "",
-                                          () => New (),
-                                          null,
-                                          null,
-                                          KeyCode.N
-                                          | KeyCode.CtrlMask
-                                          | KeyCode.AltMask
-                                         ),
-                                     new ("_Open", "", () => Open ()),
-                                     new ("_Save", "", () => Save ()),
-                                     new ("Save _As", "", () => SaveAs ()),
-                                     new ("_Close", "", () => Close ()),
-                                     new ("_Quit", "", () => Quit ())
-                                 }
-                                ),
-                new MenuBarItem (
-                                 "_About",
-                                 "",
-                                 () => MessageBox.Query ("Notepad", "About Notepad...", "Ok")
-                                )
-            ]
-        };
-        Application.Top.Add (menu);
-
-        _tabView = CreateNewTabView ();
-
-        _tabView.Style.ShowBorder = true;
-        _tabView.ApplyStyleChanges ();
-
-        // Start with only a single view but support splitting to show side by side
-        var split = new TileView (1) { X = 0, Y = 1, Width = Dim.Fill (), Height = Dim.Fill (1) };
-        split.Tiles.ElementAt (0).ContentView.Add (_tabView);
-        split.LineStyle = LineStyle.None;
-
-        Application.Top.Add (split);
-
-        _lenStatusItem = new StatusItem (KeyCode.CharMask, "Len: ", null);
-
-        var statusBar = new StatusBar (
-                                       new []
-                                       {
-                                           new (
-                                                Application.QuitKey,
-                                                $"{Application.QuitKey} to Quit",
-                                                () => Quit ()
-                                               ),
-
-                                           // These shortcut keys don't seem to work correctly in linux 
-                                           //new StatusItem(Key.CtrlMask | Key.N, "~^O~ Open", () => Open()),
-                                           //new StatusItem(Key.CtrlMask | Key.N, "~^N~ New", () => New()),
-
-                                           new (KeyCode.CtrlMask | KeyCode.S, "~^S~ Save", () => Save ()),
-                                           new (KeyCode.CtrlMask | KeyCode.W, "~^W~ Close", () => Close ()),
-                                           _lenStatusItem
-                                       }
-                                      );
-        _focusedTabView = _tabView;
-        _tabView.SelectedTabChanged += TabView_SelectedTabChanged;
-        _tabView.Enter += (s, e) => _focusedTabView = _tabView;
-
-        Application.Top.Add (statusBar);
-        Application.Top.Ready += (s, e) => New ();
+        return true;
     }
 
     private void Close () { Close (_focusedTabView, _focusedTabView.SelectedTab); }
@@ -240,7 +248,7 @@ public class Notepad : Scenario
         return tv;
     }
 
-    private void New () { Open (null, $"new {_numbeOfNewTabs++}"); }
+    private void New () { Open (null, $"new {_numNewTabs++}"); }
 
     private void Open ()
     {
@@ -248,19 +256,23 @@ public class Notepad : Scenario
 
         Application.Run (open);
 
-        if (!open.Canceled)
+        bool canceled = open.Canceled;
+
+        if (!canceled)
         {
             foreach (string path in open.FilePaths)
             {
                 if (string.IsNullOrEmpty (path) || !File.Exists (path))
                 {
-                    return;
+                    break;
                 }
 
                 // TODO should open in focused TabView
-                Open (new FileInfo (path), Path.GetFileName (path));
+                Open (new (path), Path.GetFileName (path));
             }
         }
+
+        open.Dispose ();
     }
 
     /// <summary>Creates a new tab with initial text</summary>
@@ -327,27 +339,27 @@ public class Notepad : Scenario
 
         if (e.Tab == null)
         {
-            items = new MenuBarItem (
-                                     new MenuItem [] { new ("Open", "", () => Open ()) }
-                                    );
+            items = new (
+                         new MenuItem [] { new ("Open", "", () => Open ()) }
+                        );
         }
         else
         {
             var tv = (TabView)sender;
             var t = (OpenedFile)e.Tab;
 
-            items = new MenuBarItem (
-                                     new MenuItem []
-                                     {
-                                         new ("Save", "", () => Save (_focusedTabView, e.Tab)),
-                                         new ("Close", "", () => Close (tv, e.Tab)),
-                                         null,
-                                         new ("Split Up", "", () => SplitUp (tv, t)),
-                                         new ("Split Down", "", () => SplitDown (tv, t)),
-                                         new ("Split Right", "", () => SplitRight (tv, t)),
-                                         new ("Split Left", "", () => SplitLeft (tv, t))
-                                     }
-                                    );
+            items = new (
+                         new MenuItem []
+                         {
+                             new ("Save", "", () => Save (_focusedTabView, e.Tab)),
+                             new ("Close", "", () => Close (tv, e.Tab)),
+                             null,
+                             new ("Split Up", "", () => SplitUp (tv, t)),
+                             new ("Split Down", "", () => SplitDown (tv, t)),
+                             new ("Split Right", "", () => SplitRight (tv, t)),
+                             new ("Split Left", "", () => SplitLeft (tv, t))
+                         }
+                        );
         }
 
         Rectangle screen = ((View)sender).BoundsToScreen (new (e.MouseEvent.X, e.MouseEvent.Y, 0, 0));
@@ -360,14 +372,6 @@ public class Notepad : Scenario
 
     private class OpenedFile : Tab
     {
-        public FileInfo File { get; set; }
-
-        /// <summary>The text of the tab the last time it was saved</summary>
-        /// <value></value>
-        public string SavedText { get; set; }
-
-        public bool UnsavedChanges => !string.Equals (SavedText, View.Text);
-
         public OpenedFile CloneTo (TabView other)
         {
             var newTab = new OpenedFile { DisplayText = base.Text, File = File };
@@ -399,6 +403,8 @@ public class Notepad : Scenario
             };
         }
 
+        public FileInfo File { get; set; }
+
         public void RegisterTextViewEvents (TabView parent)
         {
             var textView = (TextView)View;
@@ -428,6 +434,12 @@ public class Notepad : Scenario
                               };
         }
 
+        /// <summary>The text of the tab the last time it was saved</summary>
+        /// <value></value>
+        public string SavedText { get; set; }
+
+        public bool UnsavedChanges => !string.Equals (SavedText, View.Text);
+
         internal void Save ()
         {
             string newText = View.Text;

+ 7 - 3
UICatalog/Scenarios/ProgressBarStyles.cs

@@ -27,6 +27,8 @@ public class ProgressBarStyles : Scenario
         ConfigurationManager.Themes.Theme = Theme;
         ConfigurationManager.Apply ();
 
+        Top = new ();
+
         var editor = new AdornmentsEditor
         {
             Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}", BorderStyle = LineStyle.Single
@@ -72,12 +74,13 @@ public class ProgressBarStyles : Scenario
 
                                          dialog.Bounds = new Rectangle (0, 0, colorPicker.Frame.Width, colorPicker.Frame.Height);
 
-                                         Application.Top.LayoutSubviews ();
+                                         Top.LayoutSubviews ();
                                      };
 
             dialog.Add (colorPicker);
             colorPicker.ColorChanged += (s, e) => { dialog.RequestStop (); };
             Application.Run (dialog);
+            dialog.Dispose ();
 
             ColorName retColor = colorPicker.SelectedColor;
             colorPicker.Dispose ();
@@ -274,7 +277,7 @@ public class ProgressBarStyles : Scenario
                                  300
                                 );
 
-        Application.Top.Unloaded += Top_Unloaded;
+        Top.Unloaded += Top_Unloaded;
 
         void Top_Unloaded (object sender, EventArgs args)
         {
@@ -290,10 +293,11 @@ public class ProgressBarStyles : Scenario
                 _pulseTimer = null;
             }
 
-            Application.Top.Unloaded -= Top_Unloaded;
+            Top.Unloaded -= Top_Unloaded;
         }
 
         Application.Run (editor);
+        editor.Dispose ();
         Application.Shutdown ();
     }
 

+ 6 - 3
UICatalog/Scenarios/RunTExample.cs

@@ -6,13 +6,16 @@ namespace UICatalog.Scenarios;
 [ScenarioCategory ("Top Level Windows")]
 public class RunTExample : Scenario
 {
-    public override void Run () { Application.Run<ExampleWindow> (); }
-
-    public override void Setup ()
+    public override void Init ()
     {
         // No need to call Init if Application.Run<T> is used
+        Application.Run<ExampleWindow> ();
+
+        Application.Top.Dispose ();
     }
 
+    public override void Run () { }
+
     public class ExampleWindow : Window
     {
         private readonly TextField _usernameText;

+ 4 - 2
UICatalog/Scenarios/RuneWidthGreaterThanOne.cs

@@ -21,6 +21,8 @@ public class RuneWidthGreaterThanOne : Scenario
     {
         Application.Init ();
 
+        Top = new ();
+
         var menu = new MenuBar
         {
             Menus =
@@ -85,13 +87,13 @@ public class RuneWidthGreaterThanOne : Scenario
         };
         _win = new Window { X = 5, Y = 5, Width = Dim.Fill (22), Height = Dim.Fill (5) };
         _win.Add (_label, _text, _button, _labelR, _labelV);
-        Application.Top.Add (menu, _win);
+        Top.Add (menu, _win);
 
         WideRunes ();
 
         //NarrowRunes ();
         //MixedRunes ();
-        Application.Run ();
+        Application.Run (Top);
     }
 
     public override void Run () { }

+ 4 - 4
UICatalog/Scenarios/Scrolling.cs

@@ -78,10 +78,10 @@ public class Scrolling : Scenario
             verticalRuler.Text =
                 vrule.Repeat ((int)Math.Ceiling (verticalRuler.Bounds.Height * 2 / (double)rule.Length))
                     [..(verticalRuler.Bounds.Height * 2)];
-            Application.Top.Loaded -= Top_Loaded;
+            Top.Loaded -= Top_Loaded;
         }
 
-        Application.Top.Loaded += Top_Loaded;
+        Top.Loaded += Top_Loaded;
 
         var pressMeButton = new Button { X = 3, Y = 3, Text = "Press me!" };
         pressMeButton.Accept += (s, e) => MessageBox.Query (20, 7, "MessageBox", "Neat?", "Yes", "No");
@@ -270,9 +270,9 @@ public class Scrolling : Scenario
         void Top_Unloaded (object sender, EventArgs args)
         {
             pulsing = false;
-            Application.Top.Unloaded -= Top_Unloaded;
+            Top.Unloaded -= Top_Unloaded;
         }
 
-        Application.Top.Unloaded += Top_Unloaded;
+        Top.Unloaded += Top_Unloaded;
     }
 }

+ 33 - 11
UICatalog/Scenarios/SingleBackgroundWorker.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
+using System.Diagnostics;
 using System.Threading;
 using Terminal.Gui;
 
@@ -11,15 +12,15 @@ namespace UICatalog.Scenarios;
 [ScenarioCategory ("Top Level Windows")]
 public class SingleBackgroundWorker : Scenario
 {
-    public override void Run ()
+    public override void Init ()
     {
-        Application.Top.Dispose ();
-
         Application.Run<MainApp> ();
 
         Application.Top.Dispose ();
     }
 
+    public override void Run () { }
+
     public class MainApp : Toplevel
     {
         private readonly ListView _listLog;
@@ -77,9 +78,9 @@ public class SingleBackgroundWorker : Scenario
                                           );
             Add (statusBar);
 
-            var top = new Toplevel ();
+            var workerLogTop = new Toplevel () { Title = "Worker Log Top"};
 
-            top.Add (
+            workerLogTop.Add (
                      new Label { X = Pos.Center (), Y = 0, Text = "Worker Log" }
                     );
 
@@ -91,8 +92,9 @@ public class SingleBackgroundWorker : Scenario
                 Height = Dim.Fill (),
                 Source = new ListWrapper (_log)
             };
-            top.Add (_listLog);
-            Add (top);
+            workerLogTop.Add (_listLog);
+            Add (workerLogTop);
+            Title = "MainApp";
         }
 
         private void RunWorker ()
@@ -193,24 +195,32 @@ public class SingleBackgroundWorker : Scenario
 
                                                   var builderUI =
                                                       new StagingUIController (_startStaging, e.Result as List<string>);
+                                                  var top = Application.Top;
+                                                  top.Visible = false;
+                                                  Application.Current.Visible = false;
                                                   builderUI.Load ();
+                                                  builderUI.Dispose ();
+                                                  top.Visible = true;
                                               }
 
                                               _worker = null;
                                           };
             _worker.RunWorkerAsync ();
             Application.Run (md);
+            md.Dispose ();
         }
     }
 
     public class StagingUIController : Window
     {
-        private readonly Toplevel _top;
+        private Toplevel _top;
 
         public StagingUIController (DateTime? start, List<string> list)
         {
-            Rectangle frame = Application.Top.Frame;
-            _top = new Toplevel { X = frame.X, Y = frame.Y, Width = frame.Width, Height = frame.Height };
+            _top = new Toplevel
+            {
+                Title = "_top", Width = Dim.Fill (), Height = Dim.Fill ()
+            };
 
             _top.KeyDown += (s, e) =>
                             {
@@ -299,6 +309,18 @@ public class SingleBackgroundWorker : Scenario
             _top.Add (this);
         }
 
-        public void Load () { Application.Run (_top); }
+        public void Load () {
+            Application.Run (_top);
+            _top.Dispose ();
+            _top = null;
+        }
+
+        ///// <inheritdoc />
+        //protected override void Dispose (bool disposing)
+        //{
+        //    _top?.Dispose ();
+        //    _top = null;
+        //    base.Dispose (disposing);
+        //}
     }
 }

+ 1 - 1
UICatalog/Scenarios/Sliders.cs

@@ -477,6 +477,6 @@ public class Sliders : Scenario
         #endregion Config Slider
 
         Win.FocusFirst ();
-        Application.Top.Initialized += (s, e) => Application.Top.LayoutSubviews ();
+        Top.Initialized += (s, e) => Top.LayoutSubviews ();
     }
 }

+ 2 - 2
UICatalog/Scenarios/SpinnerStyles.cs

@@ -159,7 +159,7 @@ public class SpinnerViewStyles : Scenario
 
         ckbBounce.Toggled += (s, e) => { spinner.SpinBounce = (bool)!e.OldValue; };
 
-        Application.Top.Unloaded += Top_Unloaded;
+        Top.Unloaded += Top_Unloaded;
 
         void SetCustom ()
         {
@@ -200,7 +200,7 @@ public class SpinnerViewStyles : Scenario
                 spinner = null;
             }
 
-            Application.Top.Unloaded -= Top_Unloaded;
+            Top.Unloaded -= Top_Unloaded;
         }
     }
 

+ 2 - 2
UICatalog/Scenarios/SyntaxHighlighting.cs

@@ -164,7 +164,7 @@ public class SyntaxHighlighting : Scenario
                                 )
             ]
         };
-        Application.Top.Add (menu);
+        Top.Add (menu);
 
         _textView = new TextView { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill () };
 
@@ -183,7 +183,7 @@ public class SyntaxHighlighting : Scenario
                                        }
                                       );
 
-        Application.Top.Add (statusBar);
+        Top.Add (statusBar);
     }
 
     /// <summary>

+ 2 - 2
UICatalog/Scenarios/TabViewExample.cs

@@ -67,7 +67,7 @@ public class TabViewExample : Scenario
                                 )
             ]
         };
-        Application.Top.Add (menu);
+        Top.Add (menu);
 
         _tabView = new TabView
         {
@@ -181,7 +181,7 @@ public class TabViewExample : Scenario
                                                )
                                        }
                                       );
-        Application.Top.Add (statusBar);
+        Top.Add (statusBar);
     }
 
     private void AddBlankTab () { _tabView.AddTab (new Tab (), false); }

+ 4 - 2
UICatalog/Scenarios/TableEditor.cs

@@ -669,7 +669,7 @@ public class TableEditor : Scenario
             ]
         };
 
-        Application.Top.Add (menu);
+        Top.Add (menu);
 
         var statusBar = new StatusBar (
                                        new StatusItem []
@@ -696,7 +696,7 @@ public class TableEditor : Scenario
                                                )
                                        }
                                       );
-        Application.Top.Add (statusBar);
+        Top.Add (statusBar);
 
         Win.Add (_tableView);
 
@@ -886,6 +886,7 @@ public class TableEditor : Scenario
         tf.SetFocus ();
 
         Application.Run (d);
+        d.Dispose ();
 
         if (okPressed)
         {
@@ -1085,6 +1086,7 @@ public class TableEditor : Scenario
         tf.SetFocus ();
 
         Application.Run (d);
+        d.Dispose ();
 
         if (accepted)
         {

+ 1 - 1
UICatalog/Scenarios/TextFormatterDemo.cs

@@ -57,7 +57,7 @@ public class TextFormatterDemo : Scenario
             X = 0,
             Y = Pos.Bottom (blockText) + 1,
             Text = "Unicode",
-            Checked = Application.Top.HotKeySpecifier == (Rune)' '
+            Checked = Top.HotKeySpecifier == (Rune)' '
         };
 
         Win.Add (unicodeCheckBox);

+ 2 - 2
UICatalog/Scenarios/TextViewAutocompletePopup.cs

@@ -51,7 +51,7 @@ public class TextViewAutocompletePopup : Scenario
                                 )
             ]
         };
-        Application.Top.Add (menu);
+        Top.Add (menu);
 
         _textViewTopLeft = new TextView { Width = width, Height = _height, Text = text };
         _textViewTopLeft.DrawContent += TextViewTopLeft_DrawContent;
@@ -105,7 +105,7 @@ public class TextViewAutocompletePopup : Scenario
                                            _siWrap = new StatusItem (KeyCode.Null, "", null)
                                        }
                                       );
-        Application.Top.Add (statusBar);
+        Top.Add (statusBar);
 
         Win.LayoutStarted += Win_LayoutStarted;
     }

+ 2 - 2
UICatalog/Scenarios/Threading.cs

@@ -118,10 +118,10 @@ public class Threading : Scenario
         void Top_Loaded (object sender, EventArgs args)
         {
             _btnActionCancel.SetFocus ();
-            Application.Top.Loaded -= Top_Loaded;
+            Top.Loaded -= Top_Loaded;
         }
 
-        Application.Top.Loaded += Top_Loaded;
+        Top.Loaded += Top_Loaded;
     }
 
     private async void CallLoadItemsAsync ()

+ 1 - 1
UICatalog/Scenarios/TileViewNesting.cs

@@ -62,7 +62,7 @@ public class TileViewNesting : Scenario
 
         SetupTileView ();
 
-        Application.Top.Add (menu);
+        Top.Add (menu);
 
         Win.Loaded += (s, e) => _loaded = true;
     }

+ 2 - 2
UICatalog/Scenarios/TreeUseCases.cs

@@ -47,7 +47,7 @@ public class TreeUseCases : Scenario
             ]
         };
 
-        Application.Top.Add (menu);
+        Top.Add (menu);
 
         var statusBar = new StatusBar (
                                        new StatusItem []
@@ -60,7 +60,7 @@ public class TreeUseCases : Scenario
                                        }
                                       );
 
-        Application.Top.Add (statusBar);
+        Top.Add (statusBar);
 
         // Start with the most basic use case
         LoadSimpleNodes ();

+ 1 - 1
UICatalog/Scenarios/TreeViewFileSystem.cs

@@ -172,7 +172,7 @@ public class TreeViewFileSystem : Scenario
                                 )
             ]
         };
-        Application.Top.Add (menu);
+        Top.Add (menu);
 
         _treeViewFiles = new TreeView<IFileSystemInfo> { X = 0, Y = 0, Width = Dim.Percent (50), Height = Dim.Fill () };
         _treeViewFiles.DrawLine += TreeViewFiles_DrawLine;

+ 2 - 2
UICatalog/Scenarios/Unicode.cs

@@ -60,7 +60,7 @@ public class UnicodeInMenu : Scenario
                                 )
             ]
         };
-        Application.Top.Add (menu);
+        Top.Add (menu);
 
         var statusBar = new StatusBar (
                                        new StatusItem []
@@ -74,7 +74,7 @@ public class UnicodeInMenu : Scenario
                                            new (KeyCode.Null, "~F3~ Со_хранить", null)
                                        }
                                       );
-        Application.Top.Add (statusBar);
+        Top.Add (statusBar);
 
         var label = new Label { X = 0, Y = 1, Text = "Label:" };
         Win.Add (label);

+ 8 - 7
UICatalog/Scenarios/ViewExperiments.cs

@@ -14,7 +14,8 @@ public class ViewExperiments : Scenario
         Application.Init ();
         ConfigurationManager.Themes.Theme = Theme;
         ConfigurationManager.Apply ();
-        Application.Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
+        Top = new ();
+        Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
     }
 
     public override void Setup ()
@@ -27,7 +28,7 @@ public class ViewExperiments : Scenario
             Width = Dim.Fill (),
             Height = 3
         };
-        Application.Top.Add (containerLabel);
+        Top.Add (containerLabel);
 
         var view = new View
         {
@@ -40,7 +41,7 @@ public class ViewExperiments : Scenario
             Id = "DaView"
         };
 
-        //Application.Top.Add (view);
+        //Top.Add (view);
 
         view.Margin.Thickness = new Thickness (2, 2, 2, 2);
         view.Margin.ColorScheme = Colors.ColorSchemes ["Toplevel"];
@@ -216,9 +217,9 @@ public class ViewExperiments : Scenario
                                {
                                    containerLabel.Text =
                                        $"Container.Frame: {
-                                           Application.Top.Frame
+                                           Top.Frame
                                        } .Bounds: {
-                                           Application.Top.Bounds
+                                           Top.Bounds
                                        }\nView.Frame: {
                                            view.Frame
                                        } .Bounds: {
@@ -243,11 +244,11 @@ public class ViewExperiments : Scenario
             ViewToEdit = view
         };
 
-        Application.Top.Add (editor);
+        Top.Add (editor);
         view.X = 36;
         view.Y = 4;
         view.Width = Dim.Fill ();
         view.Height = Dim.Fill ();
-        Application.Top.Add (view);
+        Top.Add (view);
     }
 }

+ 5 - 5
UICatalog/Scenarios/WindowsAndFrameViews.cs

@@ -27,7 +27,7 @@ public class WindowsAndFrameViews : Scenario
         List<View> listWin = new ();
 
         //Ignore the Win that UI Catalog created and create a new one
-        Application.Top.Remove (Win);
+        Top.Remove (Win);
         Win?.Dispose ();
 
         Win = new Window
@@ -61,7 +61,7 @@ public class WindowsAndFrameViews : Scenario
                      Text = "Press ME! (Y = Pos.AnchorEnd(1))"
                  }
                 );
-        Application.Top.Add (Win);
+        Top.Add (Win);
 
         // add it to our list
         listWin.Add (Win);
@@ -130,7 +130,7 @@ public class WindowsAndFrameViews : Scenario
                           );
             win.Add (frameView);
 
-            Application.Top.Add (win);
+            Top.Add (win);
             listWin.Add (win);
         }
 
@@ -212,9 +212,9 @@ public class WindowsAndFrameViews : Scenario
 
         frame.Add (subFrameViewofFV);
 
-        Application.Top.Add (frame);
+        Top.Add (frame);
         listWin.Add (frame);
 
-        Application.Top.ColorScheme = Colors.ColorSchemes ["Base"];
+        Top.ColorScheme = Colors.ColorSchemes ["Base"];
     }
 }

+ 4 - 3
UICatalog/Scenarios/WizardAsView.cs

@@ -52,7 +52,8 @@ public class WizardAsView : Scenario
                                 )
             ]
         };
-        Application.Top.Add (menu);
+        Top = new ();
+        Top.Add (menu);
 
         // No need for a Title because the border is disabled
         var wizard = new Wizard { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill () };
@@ -138,8 +139,8 @@ public class WizardAsView : Scenario
         lastStep.HelpText =
             "The wizard is complete!\n\nPress the Finish button to continue.\n\nPressing Esc will cancel.";
 
-        Application.Top.Add (wizard);
-        Application.Run (Application.Top);
+        Top.Add (wizard);
+        Application.Run (Top);
     }
 
     public override void Run ()

+ 3 - 2
UICatalog/Scenarios/Wizards.cs

@@ -81,10 +81,10 @@ public class Wizards : Scenario
         void Top_Loaded (object sender, EventArgs args)
         {
             frame.Height = widthEdit.Frame.Height + heightEdit.Frame.Height + titleEdit.Frame.Height + 2;
-            Application.Top.Loaded -= Top_Loaded;
+            Top.Loaded -= Top_Loaded;
         }
 
-        Application.Top.Loaded += Top_Loaded;
+        Top.Loaded += Top_Loaded;
 
         label = new Label
         {
@@ -349,6 +349,7 @@ public class Wizards : Scenario
                                                                                     };
 
                                             Application.Run (wizard);
+                                            wizard.Dispose ();
                                         }
                                         catch (FormatException)
                                         {

+ 7 - 11
UICatalog/UICatalog.cs

@@ -199,7 +199,7 @@ internal class UICatalogApp
             Apply ();
         }
 
-        Application.Run<UICatalogTopLevel> ();
+        Application.Run<UICatalogTopLevel> ().Dispose ();
         Application.Shutdown ();
 
         return _selectedScenario!;
@@ -290,11 +290,10 @@ internal class UICatalogApp
             Application.Init (driverName: _forceDriver);
             _selectedScenario.Theme = _cachedTheme;
             _selectedScenario.TopLevelColorScheme = _topLevelColorScheme;
-            _selectedScenario.Init ();
-            _selectedScenario.Setup ();
-            _selectedScenario.Run ();
+            _selectedScenario.Main ();
             _selectedScenario.Dispose ();
             _selectedScenario = null;
+            // TODO: Throw if shutdown was not called already
             Application.Shutdown ();
             VerifyObjectsWereDisposed ();
 
@@ -322,13 +321,12 @@ internal class UICatalogApp
             Apply ();
             scenario.Theme = _cachedTheme;
             scenario.TopLevelColorScheme = _topLevelColorScheme;
-            scenario.Init ();
-            scenario.Setup ();
-            scenario.Run ();
+            scenario.Main ();
             scenario.Dispose ();
 
             // This call to Application.Shutdown brackets the Application.Init call
             // made by Scenario.Init() above
+            // TODO: Throw if shutdown was not called already
             Application.Shutdown ();
 
             VerifyObjectsWereDisposed ();
@@ -464,10 +462,6 @@ internal class UICatalogApp
                              _selectedScenario = null;
                              RequestStop ();
                          }
-                         else
-                         {
-                             _selectedScenario.RequestStop ();
-                         }
                      }
                     ),
                 new (
@@ -992,6 +986,7 @@ internal class UICatalogApp
                            {
                                var dlg = new KeyBindingsDialog ();
                                Application.Run (dlg);
+                               dlg.Dispose ();
                            };
 
             menuItems.Add (null!);
@@ -1069,6 +1064,7 @@ internal class UICatalogApp
         {
             Applied -= ConfigAppliedHandler;
             Unloaded -= UnloadedHandler;
+            Dispose ();
         }
     }
 

+ 207 - 32
UnitTests/Application/ApplicationTests.cs

@@ -39,36 +39,39 @@ public class ApplicationTests
     [AutoInitShutdown]
     public void Begin_Sets_Application_Top_To_Console_Size ()
     {
-        Assert.Equal (new Rectangle (0, 0, 80, 25), Application.Top.Frame);
-        Application.Begin (Application.Top);
+        Assert.Null (Application.Top);
+        Application.Begin (new ());
         Assert.Equal (new Rectangle (0, 0, 80, 25), Application.Top.Frame);
         ((FakeDriver)Application.Driver).SetBufferSize (5, 5);
         Assert.Equal (new Rectangle (0, 0, 5, 5), Application.Top.Frame);
     }
 
     [Fact]
-    public void End_Should_Not_Dispose_ApplicationTop_Shutdown_Should ()
+    public void End_And_Shutdown_Should_Not_Dispose_ApplicationTop ()
     {
         Init ();
 
-        RunState rs = Application.Begin (Application.Top);
+        RunState rs = Application.Begin (new ());
+        Assert.Equal (rs.Toplevel, Application.Top);
         Application.End (rs);
 
 #if DEBUG_IDISPOSABLE
         Assert.True (rs.WasDisposed);
-        Assert.False (Application.Top.WasDisposed);
+        Assert.False (Application.Top.WasDisposed); // Is true because the rs.Toplevel is the same as Application.Top
 #endif
 
         Assert.Null (rs.Toplevel);
 
         var top = Application.Top;
 
-        Shutdown ();
-
 #if DEBUG_IDISPOSABLE
+        var exception = Record.Exception (() => Shutdown ());
+        Assert.NotNull (exception);
+        Assert.False (top.WasDisposed);
+        top.Dispose ();
         Assert.True (top.WasDisposed);
 #endif
-
+        Shutdown ();
         Assert.Null (Application.Top);
     }
 
@@ -107,6 +110,7 @@ public class ApplicationTests
         Assert.NotNull (Application.MainLoop);
         Assert.NotNull (Application.Driver);
 
+        topLevel.Dispose ();
         Shutdown ();
 
         Assert.Null (Application.Top);
@@ -126,6 +130,7 @@ public class ApplicationTests
         var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
         Application.Init (driverName: driverType.Name);
         Assert.NotNull (Application.Driver);
+        Assert.NotEqual(driver, Application.Driver);
         Assert.Equal (driverType, Application.Driver.GetType ());
         Shutdown ();
     }
@@ -264,7 +269,6 @@ public class ApplicationTests
         Assert.Throws<InvalidOperationException> (
                                                   () =>
                                                       Application.InternalInit (
-                                                                                () => topLevel = new TestToplevel (),
                                                                                 new FakeDriver ()
                                                                                )
                                                  );
@@ -276,7 +280,7 @@ public class ApplicationTests
 
         // Now try the other way
         topLevel = null;
-        Application.InternalInit (() => topLevel = new TestToplevel (), new FakeDriver ());
+        Application.InternalInit (new FakeDriver ());
 
         Assert.Throws<InvalidOperationException> (() => Application.Init (new FakeDriver ()));
         Shutdown ();
@@ -287,7 +291,7 @@ public class ApplicationTests
     }
 
     [Fact]
-    public void InitWithTopLevelFactory_Begin_End_Cleans_Up ()
+    public void InitWithoutTopLevelFactory_Begin_End_Cleans_Up ()
     {
         // Begin will cause Run() to be called, which will call Begin(). Thus will block the tests
         // if we don't stop
@@ -295,8 +299,8 @@ public class ApplicationTests
 
         // NOTE: Run<T>, when called after Init has been called behaves differently than
         // when called if Init has not been called.
-        Toplevel topLevel = null;
-        Application.InternalInit (() => topLevel = new TestToplevel (), new FakeDriver ());
+        Toplevel topLevel = new ();
+        Application.InternalInit (new FakeDriver ());
 
         RunState runstate = null;
 
@@ -323,6 +327,7 @@ public class ApplicationTests
         Assert.NotNull (Application.MainLoop);
         Assert.NotNull (Application.Driver);
 
+        topLevel.Dispose ();
         Shutdown ();
 
         Assert.Null (Application.Top);
@@ -335,8 +340,8 @@ public class ApplicationTests
     public void Internal_Properties_Correct ()
     {
         Assert.True (Application._initialized);
-        Assert.NotNull (Application.Top);
-        RunState rs = Application.Begin (Application.Top);
+        Assert.Null (Application.Top);
+        RunState rs = Application.Begin (new ());
         Assert.Equal (Application.Top, rs.Toplevel);
         Assert.Null (Application.MouseGrabView); // public
         Assert.Null (Application.WantContinuousButtonPressedView); // public
@@ -358,6 +363,7 @@ public class ApplicationTests
         Application.MainLoop.Running = true;
         Application.RunIteration (ref rs, ref firstIteration);
         Assert.Equal (1, actionCalled);
+        top.Dispose ();
         Application.Shutdown ();
     }
 
@@ -365,6 +371,8 @@ public class ApplicationTests
     [AutoInitShutdown]
     public void SetCurrentAsTop_Run_A_Not_Modal_Toplevel_Make_It_The_Current_Application_Top ()
     {
+        var top = new Toplevel ();
+
         var t1 = new Toplevel ();
         var t2 = new Toplevel ();
         var t3 = new Toplevel ();
@@ -456,6 +464,13 @@ public class ApplicationTests
         Application.Run (t1);
 
         Assert.Equal (t1, Application.Top);
+        // top wasn't run and so never was added to toplevel's stack
+        Assert.NotEqual (top, Application.Top);
+#if DEBUG_IDISPOSABLE
+        Assert.False (Application.Top.WasDisposed);
+        t1.Dispose ();
+        Assert.True (Application.Top.WasDisposed);
+#endif
     }
 
     private void Init ()
@@ -497,14 +512,19 @@ public class ApplicationTests
 
     [Fact]
 
-    public void Run_T_After_InitWithDriver_with_TopLevel_Throws ()
+    public void Run_T_After_InitWithDriver_with_TopLevel_Does_Not_Throws ()
     {
         // Setup Mock driver
         Init ();
 
-        // Run<Toplevel> when already initialized with a Driver will throw (because Toplevel is not derived from TopLevel)
-        Assert.Throws<ArgumentException> (() => Application.Run<Toplevel> ());
+        Application.Iteration += (s, e) => Application.RequestStop ();
 
+        // Run<Toplevel> when already initialized or not with a Driver will not throw (because Window is derived from Toplevel)
+        // Using another type not derived from Toplevel will throws at compile time
+        Application.Run<Window> ();
+        Assert.True (Application.Top is Window);
+
+        Application.Top.Dispose ();
         Shutdown ();
 
         Assert.Null (Application.Top);
@@ -514,14 +534,24 @@ public class ApplicationTests
 
     [Fact]
 
-    public void Run_T_After_InitWithDriver_with_TopLevel_and_Driver_Throws ()
+    public void Run_T_After_InitWithDriver_with_TopLevel_and_Driver_Does_Not_Throws ()
     {
         // Setup Mock driver
         Init ();
 
-        // Run<Toplevel> when already initialized with a Driver will throw (because Toplevel is not derivied from TopLevel)
-        Assert.Throws<ArgumentException> (() => Application.Run<Toplevel> (null, new FakeDriver ()));
+        Application.Iteration += (s, e) => Application.RequestStop ();
+
+        // Run<Toplevel> when already initialized or not with a Driver will not throw (because Window is derived from Toplevel)
+        // Using another type not derived from Toplevel will throws at compile time
+        Application.Run<Window> (null, new FakeDriver ());
+        Assert.True (Application.Top is Window);
 
+        Application.Top.Dispose ();
+        // Run<Toplevel> when already initialized or not with a Driver will not throw (because Dialog is derived from Toplevel)
+        Application.Run<Dialog> (null, new FakeDriver ());
+        Assert.True (Application.Top is Dialog);
+
+        Application.Top.Dispose ();
         Shutdown ();
 
         Assert.Null (Application.Top);
@@ -532,18 +562,19 @@ public class ApplicationTests
     [Fact]
     [TestRespondersDisposed]
 
-    public void Run_T_After_Init_Disposes_Application_Top ()
+    public void Run_T_After_Init_Does_Not_Disposes_Application_Top ()
     {
         Init ();
 
-        // Init created a Toplevel and assigned it to Application.Top
-        var initTop = Application.Top;
+        // Init doesn't create a Toplevel and assigned it to Application.Top
+        // but Begin does
+        var initTop = new Toplevel ();
 
         Application.Iteration += (s, a) =>
                                  {
                                      Assert.NotEqual(initTop, Application.Top);
 #if DEBUG_IDISPOSABLE
-                                     Assert.True (initTop.WasDisposed);
+                                     Assert.False (initTop.WasDisposed);
 #endif
                                      Application.RequestStop ();
                                  };
@@ -551,9 +582,11 @@ public class ApplicationTests
         Application.Run<TestToplevel> ();
 
 #if DEBUG_IDISPOSABLE
+        Assert.False (initTop.WasDisposed);
+        initTop.Dispose ();
         Assert.True (initTop.WasDisposed);
 #endif
-
+        Application.Top.Dispose ();
         Shutdown ();
 
         Assert.Null (Application.Top);
@@ -574,6 +607,7 @@ public class ApplicationTests
         // Init has been called and we're passing no driver to Run<TestTopLevel>. This is ok.
         Application.Run<TestToplevel> ();
 
+        Application.Top.Dispose ();
         Shutdown ();
 
         Assert.Null (Application.Top);
@@ -596,6 +630,7 @@ public class ApplicationTests
         // Init has been called, selecting FakeDriver; we're passing no driver to Run<TestTopLevel>. Should be fine.
         Application.Run<TestToplevel> ();
 
+        Application.Top.Dispose ();
         Shutdown ();
 
         Assert.Null (Application.Top);
@@ -612,8 +647,6 @@ public class ApplicationTests
 
         Application.Driver = null;
 
-        Application.Iteration += (s, a) => { Application.RequestStop (); };
-
         // Init has been called, but Driver has been set to null. Bad.
         Assert.Throws<InvalidOperationException> (() => Application.Run<TestToplevel> ());
 
@@ -635,6 +668,7 @@ public class ApplicationTests
         Application.Run<TestToplevel> ();
         Assert.Equal (typeof (FakeDriver), Application.Driver.GetType ());
 
+        Application.Top.Dispose ();
         Shutdown ();
 
         Assert.Null (Application.Top);
@@ -652,6 +686,7 @@ public class ApplicationTests
         // Init has NOT been called and we're passing a valid driver to Run<TestTopLevel>. This is ok.
         Application.Run<TestToplevel> (null, new FakeDriver ());
 
+        Application.Top.Dispose ();
         Shutdown ();
 
         Assert.Null (Application.Top);
@@ -676,6 +711,7 @@ public class ApplicationTests
 
         Application.Run (top);
 
+        top.Dispose ();
         Application.Shutdown ();
         Assert.Null (Application.Current);
         Assert.Null (Application.Top);
@@ -700,6 +736,7 @@ public class ApplicationTests
 
         Application.Run (top);
 
+        top.Dispose ();
         Application.Shutdown ();
         Assert.Null (Application.Current);
         Assert.Null (Application.Top);
@@ -713,13 +750,14 @@ public class ApplicationTests
     public void Run_Loaded_Ready_Unlodaded_Events ()
     {
         Init ();
-        Toplevel top = Application.Top;
+        Toplevel top = new ();
         var count = 0;
         top.Loaded += (s, e) => count++;
         top.Ready += (s, e) => count++;
         top.Unloaded += (s, e) => count++;
         Application.Iteration += (s, a) => Application.RequestStop ();
-        Application.Run ();
+        Application.Run (top);
+        top.Dispose ();
         Application.Shutdown ();
         Assert.Equal (3, count);
     }
@@ -733,7 +771,7 @@ public class ApplicationTests
 
         // Don't use Dialog here as it has more layout logic. Use Window instead.
         Dialog d = null;
-        Toplevel top = Application.Top;
+        Toplevel top = new ();
         top.DrawContent += (s, a) => count++;
         int iteration = -1;
 
@@ -768,7 +806,8 @@ public class ApplicationTests
                                          Application.RequestStop ();
                                      }
                                  };
-        Application.Run ();
+        Application.Run (top);
+        top.Dispose ();
         Application.Shutdown ();
 
         // 1 - First top load, 1 - Dialog load, 1 - Dialog unload, Total - 3.
@@ -873,9 +912,145 @@ public class ApplicationTests
                                               );
 
         Application.End (rs);
+        w.Dispose ();
         Application.Shutdown ();
     }
 
+    [Fact]
+    public void End_Does_Not_Dispose ()
+    {
+        Init ();
+
+        var top = new Toplevel ();
+
+        Window w = new ();
+        w.Ready += (s, e) => Application.RequestStop (); // Causes `End` to be called
+        Application.Run(w);
+
+#if DEBUG_IDISPOSABLE
+        Assert.False (w.WasDisposed);
+#endif
+
+        Assert.NotNull (w);
+        Assert.Equal (string.Empty, w.Title); // Valid - w has not been disposed. The user may want to run it again
+        Assert.NotNull (Application.Top);
+        Assert.Equal(w, Application.Top);
+        Assert.NotEqual(top, Application.Top);
+        Assert.Null (Application.Current);
+
+        Application.Run(w); // Valid - w has not been disposed.
+
+#if DEBUG_IDISPOSABLE
+        Assert.False (w.WasDisposed);
+        var exception = Record.Exception (() => Application.Shutdown()); // Invalid - w has not been disposed.
+        Assert.NotNull (exception);
+
+        w.Dispose ();
+        Assert.True (w.WasDisposed);
+        exception = Record.Exception (() => Application.Run (w)); // Invalid - w has been disposed. Run it in debug mode will throw, otherwise the user may want to run it again
+        Assert.NotNull (exception);
+
+        exception = Record.Exception (() => Assert.Equal (string.Empty, w.Title)); // Invalid - w has been disposed and cannot be accessed
+        Assert.NotNull (exception);
+        exception = Record.Exception (() => w.Title = "NewTitle"); // Invalid - w has been disposed and cannot be accessed
+        Assert.NotNull (exception);
+#endif
+        Application.Shutdown ();
+        Assert.NotNull (w);
+        Assert.Null (Application.Current);
+        Assert.NotNull (top);
+        Assert.Null (Application.Top);
+    }
+
+    [Fact]
+    public void Run_Creates_Top_Without_Init ()
+    {
+        var driver = new FakeDriver ();
+
+        Assert.Null (Application.Top);
+
+        Application.Iteration += (s, e) =>
+                                 {
+                                     Assert.NotNull (Application.Top);
+                                     Application.RequestStop ();
+                                 };
+        var top = Application.Run (null, driver);
+#if DEBUG_IDISPOSABLE
+        Assert.Equal(top, Application.Top);
+        Assert.False (top.WasDisposed);
+        var exception = Record.Exception (() => Application.Shutdown ());
+        Assert.NotNull (exception);
+        Assert.False (top.WasDisposed);
+#endif
+
+        // It's up to caller to dispose it
+        top.Dispose ();
+
+#if DEBUG_IDISPOSABLE
+        Assert.True (top.WasDisposed);
+#endif
+        Assert.NotNull (Application.Top);
+
+        Application.Shutdown ();
+        Assert.Null (Application.Top);
+    }
+
+    [Fact]
+    public void Run_T_Creates_Top_Without_Init ()
+    {
+        var driver = new FakeDriver ();
+
+        Assert.Null (Application.Top);
+
+        Application.Iteration += (s, e) =>
+                                 {
+                                     Assert.NotNull (Application.Top);
+                                     Application.RequestStop ();
+                                 };
+        Application.Run<Toplevel> (null, driver);
+#if DEBUG_IDISPOSABLE
+        Assert.False (Application.Top.WasDisposed);
+        var exception = Record.Exception (() => Application.Shutdown ());
+        Assert.NotNull (exception);
+        Assert.False (Application.Top.WasDisposed);
+        // It's up to caller to dispose it
+        Application.Top.Dispose ();
+        Assert.True (Application.Top.WasDisposed);
+#endif
+        Assert.NotNull (Application.Top);
+
+        Application.Shutdown ();
+        Assert.Null (Application.Top);
+    }
+
+    [Fact]
+    public void Run_t_Creates_Top_Without_Init ()
+    {
+        var driver = new FakeDriver ();
+
+        Assert.Null (Application.Top);
+
+        Application.Iteration += (s, e) =>
+                                 {
+                                     Assert.NotNull (Application.Top);
+                                     Application.RequestStop ();
+                                 };
+        Application.Run (new (), null, driver);
+#if DEBUG_IDISPOSABLE
+        Assert.False (Application.Top.WasDisposed);
+        var exception = Record.Exception (() => Application.Shutdown ());
+        Assert.NotNull (exception);
+        Assert.False (Application.Top.WasDisposed);
+        // It's up to caller to dispose it
+        Application.Top.Dispose ();
+        Assert.True (Application.Top.WasDisposed);
+#endif
+        Assert.NotNull (Application.Top);
+
+        Application.Shutdown ();
+        Assert.Null (Application.Top);
+    }
+
     // TODO: Add tests for Run that test errorHandler
 
     #endregion

+ 29 - 24
UnitTests/Application/KeyboardTests.cs

@@ -20,7 +20,7 @@ public class KeyboardTests
     {
         Application.Init (new FakeDriver ());
 
-        Toplevel top = Application.Top;
+        Toplevel top = new ();
         var w1 = new Window ();
         var v1 = new TextField ();
         var v2 = new TextView ();
@@ -110,6 +110,7 @@ public class KeyboardTests
         Assert.Equal (KeyCode.PageUp | KeyCode.CtrlMask, Application.AlternateBackwardKey.KeyCode);
         Assert.Equal (KeyCode.Q | KeyCode.CtrlMask, Application.QuitKey.KeyCode);
 
+        top.Dispose ();
         // Shutdown must be called to safely clean up Application if Init has been called
         Application.Shutdown ();
     }
@@ -118,7 +119,7 @@ public class KeyboardTests
     [AutoInitShutdown]
     public void EnsuresTopOnFront_CanFocus_False_By_Keyboard ()
     {
-        Toplevel top = Application.Top;
+        Toplevel top = new ();
 
         var win = new Window
         {
@@ -149,35 +150,35 @@ public class KeyboardTests
         Assert.True (win.HasFocus);
         Assert.True (win2.CanFocus);
         Assert.False (win2.HasFocus);
-        Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
+        Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
 
         win.CanFocus = false;
         Assert.False (win.CanFocus);
         Assert.False (win.HasFocus);
         Assert.True (win2.CanFocus);
         Assert.True (win2.HasFocus);
-        Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
+        Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
 
         top.NewKeyDownEvent (Key.Tab.WithCtrl);
         Assert.True (win2.CanFocus);
         Assert.False (win.HasFocus);
         Assert.True (win2.CanFocus);
         Assert.True (win2.HasFocus);
-        Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
+        Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
 
         top.NewKeyDownEvent (Key.Tab.WithCtrl);
         Assert.False (win.CanFocus);
         Assert.False (win.HasFocus);
         Assert.True (win2.CanFocus);
         Assert.True (win2.HasFocus);
-        Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
+        Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
     }
 
     [Fact]
     [AutoInitShutdown]
     public void EnsuresTopOnFront_CanFocus_True_By_Keyboard_ ()
     {
-        Toplevel top = Application.Top;
+        Toplevel top = new ();
 
         var win = new Window
         {
@@ -208,21 +209,21 @@ public class KeyboardTests
         Assert.True (win.HasFocus);
         Assert.True (win2.CanFocus);
         Assert.False (win2.HasFocus);
-        Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
+        Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
 
         top.NewKeyDownEvent (Key.Tab.WithCtrl);
         Assert.True (win.CanFocus);
         Assert.False (win.HasFocus);
         Assert.True (win2.CanFocus);
         Assert.True (win2.HasFocus);
-        Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
+        Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
 
         top.NewKeyDownEvent (Key.Tab.WithCtrl);
         Assert.True (win.CanFocus);
         Assert.True (win.HasFocus);
         Assert.True (win2.CanFocus);
         Assert.False (win2.HasFocus);
-        Assert.Equal ("win", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
+        Assert.Equal ("win", ((Window)top.Subviews [^1]).Title);
     }
 
     [Fact]
@@ -281,18 +282,19 @@ public class KeyboardTests
 
         var keyUps = 0;
         var output = string.Empty;
+        var top = new Toplevel ();
 
-        Application.Top.KeyUp += (sender, args) =>
-                                 {
-                                     if (args.KeyCode != (KeyCode.CtrlMask | KeyCode.Q))
-                                     {
-                                         output += args.AsRune;
-                                     }
+        top.KeyUp += (sender, args) =>
+                     {
+                         if (args.KeyCode != (KeyCode.CtrlMask | KeyCode.Q))
+                         {
+                             output += args.AsRune;
+                         }
 
-                                     keyUps++;
-                                 };
+                         keyUps++;
+                     };
 
-        Application.Run (Application.Top);
+        Application.Run (top);
 
         // Input string should match output
         Assert.Equal (input, output);
@@ -306,6 +308,7 @@ public class KeyboardTests
         // # of key up events should match # of iterations
         Assert.Equal (stackSize, iterations);
 
+        top.Dispose ();
         Application.Shutdown ();
         Assert.Null (Application.Current);
         Assert.Null (Application.Top);
@@ -321,8 +324,9 @@ public class KeyboardTests
         var invoked = false;
         view.InvokingKeyBindings += (s, e) => invoked = true;
 
-        Application.Top.Add (view);
-        Application.Begin (Application.Top);
+        var top = new Toplevel ();
+        top.Add (view);
+        Application.Begin (top);
 
         Application.OnKeyDown (Key.A);
         Assert.True (invoked);
@@ -364,8 +368,9 @@ public class KeyboardTests
         var invoked = false;
         view.InvokingKeyBindings += (s, e) => invoked = true;
 
-        Application.Top.Add (view);
-        Application.Begin (Application.Top);
+        var top = new Toplevel ();
+        top.Add (view);
+        Application.Begin (top);
 
         Application.OnKeyDown (Key.A.WithCtrl);
         Assert.False (invoked);
@@ -386,7 +391,7 @@ public class KeyboardTests
     [AutoInitShutdown]
     public void QuitKey_Getter_Setter ()
     {
-        Toplevel top = Application.Top;
+        Toplevel top = new ();
         var isQuiting = false;
 
         top.Closing += (s, e) =>

+ 6 - 4
UnitTests/Application/MainLoopTests.cs

@@ -625,7 +625,8 @@ public class MainLoopTests
     {
         Random r = new ();
         TextField tf = new ();
-        Application.Top.Add (tf);
+        var top = new Toplevel ();
+        top.Add (tf);
 
         const int numPasses = 5;
         const int numIncrements = 500;
@@ -634,7 +635,7 @@ public class MainLoopTests
         Task task = Task.Run (() => RunTest (r, tf, numPasses, numIncrements, pollMs));
 
         // blocks here until the RequestStop is processed at the end of the test
-        Application.Run ();
+        Application.Run (top);
 
         await task; // Propagate exception if any occurred
 
@@ -672,7 +673,8 @@ public class MainLoopTests
 
         btnLaunch.Accept += (s, e) => action ();
 
-        Application.Top.Add (btnLaunch);
+        var top = new Toplevel ();
+        top.Add (btnLaunch);
 
         int iterations = -1;
 
@@ -711,7 +713,7 @@ public class MainLoopTests
                                      }
                                  };
 
-        Application.Run ();
+        Application.Run (top);
 
         Assert.True (taskCompleted);
         Assert.Equal (clickMe, btn.Text);

+ 15 - 12
UnitTests/Application/MouseTests.cs

@@ -114,8 +114,6 @@ public class MouseTests
             Width = size.Width,
             Height = size.Height
         };
-        view.BeginInit();
-        view.EndInit();
 
         var mouseEvent = new MouseEvent { X = clickX, Y = clickY, Flags = MouseFlags.Button1Clicked };
         var mouseEventArgs = new MouseEventEventArgs (mouseEvent);
@@ -127,7 +125,10 @@ public class MouseTests
                                           clicked = true;
                                       };
 
-        Application.Top.Add (view);
+        var top = new Toplevel ();
+        top.Add (view);
+        Application.Begin (top);
+
         Application.OnMouseEvent (mouseEventArgs);
         Assert.Equal (expectedClicked, clicked);
     }
@@ -198,11 +199,12 @@ public class MouseTests
 
         var clicked = false;
 
-        Application.Top.X = 0;
-        Application.Top.Y = 0;
-        Application.Top.Width = size.Width * 2;
-        Application.Top.Height = size.Height * 2;
-        Application.Top.BorderStyle = LineStyle.None;
+        var top = new Toplevel ();
+        top.X = 0;
+        top.Y = 0;
+        top.Width = size.Width * 2;
+        top.Height = size.Height * 2;
+        top.BorderStyle = LineStyle.None;
 
         var view = new View { X = pos.X, Y = pos.Y, Width = size.Width, Height = size.Height };
 
@@ -210,8 +212,8 @@ public class MouseTests
         view.BorderStyle = LineStyle.Single;
         view.CanFocus = true;
 
-        Application.Top.Add (view);
-        Application.Begin (Application.Top);
+        top.Add (view);
+        Application.Begin (top);
         var mouseEvent = new MouseEvent { X = clickX, Y = clickY, Flags = MouseFlags.Button1Clicked };
         var mouseEventArgs = new MouseEventEventArgs (mouseEvent);
 
@@ -238,7 +240,8 @@ public class MouseTests
         var sv = new ScrollView { Width = Dim.Fill (), Height = Dim.Fill (), ContentSize = new (100, 100) };
 
         sv.Add (tf);
-        Application.Top.Add (sv);
+        var top = new Toplevel ();
+        top.Add (sv);
 
         int iterations = -1;
 
@@ -306,7 +309,7 @@ public class MouseTests
                                      }
                                  };
 
-        Application.Run ();
+        Application.Run (top);
     }
 
     [Fact]

+ 1 - 0
UnitTests/Application/RunStateTests.cs

@@ -33,6 +33,7 @@ public class RunStateTests
         Assert.NotNull (Application.MainLoop);
         Assert.NotNull (Application.Driver);
 
+        top.Dispose ();
         Shutdown ();
 
 #if DEBUG_IDISPOSABLE

+ 2 - 2
UnitTests/Application/SynchronizatonContextTests.cs

@@ -46,7 +46,7 @@ public class SyncrhonizationContextTests
                  );
 
         // blocks here until the RequestStop is processed at the end of the test
-        Application.Run ();
+        Application.Run ().Dispose ();
         Assert.True (success);
     }
 
@@ -79,7 +79,7 @@ public class SyncrhonizationContextTests
                  );
 
         // blocks here until the RequestStop is processed at the end of the test
-        Application.Run ();
+        Application.Run ().Dispose ();
         Assert.True (success);
     }
 }

+ 2 - 2
UnitTests/Configuration/ConfigurationMangerTests.cs

@@ -816,9 +816,9 @@ public class ConfigurationManagerTests
     }
 
     [Fact]
-    public void UseWithoutResetAsserts ()
+    public void UseWithoutResetDoesNotThrow ()
     {
         Initialize ();
-        Assert.Throws<InvalidOperationException> (() => _ = Settings);
+        _ = Settings;
     }
 }

+ 6 - 4
UnitTests/ConsoleDrivers/ConsoleDriverTests.cs

@@ -49,7 +49,7 @@ public class ConsoleDriverTests
 
         Console.MockKeyPresses = mKeys;
 
-        Toplevel top = Application.Top;
+        Toplevel top = new ();
         var view = new View { CanFocus = true };
         var rText = "";
         var idx = 0;
@@ -72,10 +72,11 @@ public class ConsoleDriverTests
                                      }
                                  };
 
-        Application.Run ();
+        Application.Run (top);
 
         Assert.Equal ("MockKeyPresses", rText);
 
+        top.Dispose ();
         // Shutdown must be called to safely clean up Application if Init has been called
         Application.Shutdown ();
     }
@@ -87,7 +88,7 @@ public class ConsoleDriverTests
         var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
         Application.Init (driver);
 
-        Toplevel top = Application.Top;
+        Toplevel top = new ();
         var view = new View { CanFocus = true };
         var count = 0;
         var wasKeyPressed = false;
@@ -105,10 +106,11 @@ public class ConsoleDriverTests
                                      }
                                  };
 
-        Application.Run ();
+        Application.Run (top);
 
         Assert.False (wasKeyPressed);
 
+        top.Dispose ();
         // Shutdown must be called to safely clean up Application if Init has been called
         Application.Shutdown ();
     }

+ 206 - 61
UnitTests/Dialogs/DialogTests.cs

@@ -49,6 +49,7 @@ public class DialogTests
         RunIteration (ref runstate, ref first);
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Justify
         dlg = new Dialog
@@ -73,6 +74,7 @@ public class DialogTests
         RunIteration (ref runstate, ref first);
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Right
         dlg = new Dialog
@@ -97,6 +99,7 @@ public class DialogTests
         RunIteration (ref runstate, ref first);
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Left
         dlg = new Dialog
@@ -121,6 +124,7 @@ public class DialogTests
         RunIteration (ref runstate, ref first);
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
     }
 
     [Fact]
@@ -148,7 +152,7 @@ public class DialogTests
         d.SetBufferSize (buttonRow.Length, 3);
 
         // Default - Center
-        (runstate, Dialog _) = RunButtonTestDialog (
+        (runstate, Dialog dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
                                                     Dialog.ButtonAlignments.Center,
@@ -159,54 +163,58 @@ public class DialogTests
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Justify
         buttonRow = $"{CM.Glyphs.VLine}{btn1} {btn2}  {btn3}  {btn4}{CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, Dialog _) = RunButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Dialog.ButtonAlignments.Justify,
-                                                    new Button { Text = btn1Text },
-                                                    new Button { Text = btn2Text },
-                                                    new Button { Text = btn3Text },
-                                                    new Button { Text = btn4Text }
-                                                   );
+        (runstate, dlg) = RunButtonTestDialog (
+                                                      title,
+                                                      width,
+                                                      Dialog.ButtonAlignments.Justify,
+                                                      new Button { Text = btn1Text },
+                                                      new Button { Text = btn2Text },
+                                                      new Button { Text = btn3Text },
+                                                      new Button { Text = btn4Text }
+                                                     );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Right
         buttonRow = $"{CM.Glyphs.VLine}  {btn1} {btn2} {btn3} {btn4}{CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, Dialog _) = RunButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Dialog.ButtonAlignments.Right,
-                                                    new Button { Text = btn1Text },
-                                                    new Button { Text = btn2Text },
-                                                    new Button { Text = btn3Text },
-                                                    new Button { Text = btn4Text }
-                                                   );
+        (runstate, dlg) = RunButtonTestDialog (
+                                                      title,
+                                                      width,
+                                                      Dialog.ButtonAlignments.Right,
+                                                      new Button { Text = btn1Text },
+                                                      new Button { Text = btn2Text },
+                                                      new Button { Text = btn3Text },
+                                                      new Button { Text = btn4Text }
+                                                     );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Left
         buttonRow = $"{CM.Glyphs.VLine}{btn1} {btn2} {btn3} {btn4}  {CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, Dialog _) = RunButtonTestDialog (
-                                                    title,
-                                                    width,
-                                                    Dialog.ButtonAlignments.Left,
-                                                    new Button { Text = btn1Text },
-                                                    new Button { Text = btn2Text },
-                                                    new Button { Text = btn3Text },
-                                                    new Button { Text = btn4Text }
-                                                   );
+        (runstate, dlg) = RunButtonTestDialog (
+                                                      title,
+                                                      width,
+                                                      Dialog.ButtonAlignments.Left,
+                                                      new Button { Text = btn1Text },
+                                                      new Button { Text = btn2Text },
+                                                      new Button { Text = btn3Text },
+                                                      new Button { Text = btn4Text }
+                                                     );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
     }
 
     [Fact]
@@ -249,6 +257,7 @@ public class DialogTests
         Assert.Equal (new (width, 1), dlg.Frame.Size);
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Justify
         buttonRow =
@@ -268,7 +277,7 @@ public class DialogTests
                 CM.Glyphs.VLine
             }";
 
-        (runstate, Dialog _) = RunButtonTestDialog (
+        (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
                                                     Dialog.ButtonAlignments.Justify,
@@ -279,11 +288,12 @@ public class DialogTests
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Right
         buttonRow = $"{CM.Glyphs.VLine}{CM.Glyphs.RightBracket} {btn2} {btn3} {btn4}{CM.Glyphs.VLine}";
 
-        (runstate, Dialog _) = RunButtonTestDialog (
+        (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
                                                     Dialog.ButtonAlignments.Right,
@@ -294,11 +304,12 @@ public class DialogTests
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Left
         buttonRow = $"{CM.Glyphs.VLine}{btn1} {btn2} {btn3} {CM.Glyphs.LeftBracket} n{CM.Glyphs.VLine}";
 
-        (runstate, Dialog _) = RunButtonTestDialog (
+        (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
                                                     Dialog.ButtonAlignments.Left,
@@ -309,6 +320,7 @@ public class DialogTests
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
     }
 
     [Fact]
@@ -338,7 +350,7 @@ public class DialogTests
         d.SetBufferSize (buttonRow.Length, 1);
 
         // Default - Center
-        (runstate, Dialog _) = RunButtonTestDialog (
+        (runstate, Dialog dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
                                                     Dialog.ButtonAlignments.Center,
@@ -349,12 +361,13 @@ public class DialogTests
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Justify
         buttonRow = $"{CM.Glyphs.VLine}{btn1}     {btn2}     {btn3}     {btn4}{CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, Dialog _) = RunButtonTestDialog (
+        (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
                                                     Dialog.ButtonAlignments.Justify,
@@ -365,12 +378,13 @@ public class DialogTests
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Right
         buttonRow = $"{CM.Glyphs.VLine}            {btn1} {btn2} {btn3} {btn4}{CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, Dialog _) = RunButtonTestDialog (
+        (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
                                                     Dialog.ButtonAlignments.Right,
@@ -381,12 +395,13 @@ public class DialogTests
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Left
         buttonRow = $"{CM.Glyphs.VLine}{btn1} {btn2} {btn3} {btn4}            {CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, Dialog _) = RunButtonTestDialog (
+        (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
                                                     Dialog.ButtonAlignments.Left,
@@ -397,6 +412,7 @@ public class DialogTests
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
     }
 
     [Fact]
@@ -428,7 +444,7 @@ public class DialogTests
         d.SetBufferSize (width, 3);
 
         // Default - Center
-        (runstate, Dialog _) = RunButtonTestDialog (
+        (runstate, Dialog dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
                                                     Dialog.ButtonAlignments.Center,
@@ -439,12 +455,13 @@ public class DialogTests
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Justify
         buttonRow = $"{CM.Glyphs.VLine}{btn1}     {btn2}     {btn3}     {btn4}{CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.GetColumns ());
 
-        (runstate, Dialog _) = RunButtonTestDialog (
+        (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
                                                     Dialog.ButtonAlignments.Justify,
@@ -455,12 +472,13 @@ public class DialogTests
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Right
         buttonRow = $"{CM.Glyphs.VLine}            {btn1} {btn2} {btn3} {btn4}{CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.GetColumns ());
 
-        (runstate, Dialog _) = RunButtonTestDialog (
+        (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
                                                     Dialog.ButtonAlignments.Right,
@@ -471,12 +489,13 @@ public class DialogTests
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Left
         buttonRow = $"{CM.Glyphs.VLine}{btn1} {btn2} {btn3} {btn4}            {CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.GetColumns ());
 
-        (runstate, Dialog _) = RunButtonTestDialog (
+        (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
                                                     Dialog.ButtonAlignments.Left,
@@ -487,6 +506,7 @@ public class DialogTests
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
     }
 
     [Fact]
@@ -507,7 +527,7 @@ public class DialogTests
 
         d.SetBufferSize (width, 1);
 
-        (runstate, Dialog _) = RunButtonTestDialog (
+        (runstate, Dialog dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
                                                     Dialog.ButtonAlignments.Center,
@@ -517,13 +537,14 @@ public class DialogTests
         // Center
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Justify 
         buttonRow =
             $"{CM.Glyphs.VLine}    {CM.Glyphs.LeftBracket} {btnText} {CM.Glyphs.RightBracket}{CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, Dialog _) = RunButtonTestDialog (
+        (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
                                                     Dialog.ButtonAlignments.Justify,
@@ -531,13 +552,14 @@ public class DialogTests
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Right
         buttonRow =
             $"{CM.Glyphs.VLine}    {CM.Glyphs.LeftBracket} {btnText} {CM.Glyphs.RightBracket}{CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, Dialog _) = RunButtonTestDialog (
+        (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
                                                     Dialog.ButtonAlignments.Right,
@@ -545,13 +567,14 @@ public class DialogTests
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Left
         buttonRow =
             $"{CM.Glyphs.VLine}{CM.Glyphs.LeftBracket} {btnText} {CM.Glyphs.RightBracket}    {CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, Dialog _) = RunButtonTestDialog (
+        (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
                                                     Dialog.ButtonAlignments.Left,
@@ -559,6 +582,7 @@ public class DialogTests
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Wider
         buttonRow =
@@ -567,7 +591,7 @@ public class DialogTests
 
         d.SetBufferSize (width, 1);
 
-        (runstate, Dialog _) = RunButtonTestDialog (
+        (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
                                                     Dialog.ButtonAlignments.Center,
@@ -575,13 +599,14 @@ public class DialogTests
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Justify
         buttonRow =
             $"{CM.Glyphs.VLine}      {CM.Glyphs.LeftBracket} {btnText} {CM.Glyphs.RightBracket}{CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, Dialog _) = RunButtonTestDialog (
+        (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
                                                     Dialog.ButtonAlignments.Justify,
@@ -589,13 +614,14 @@ public class DialogTests
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Right
         buttonRow =
             $"{CM.Glyphs.VLine}      {CM.Glyphs.LeftBracket} {btnText} {CM.Glyphs.RightBracket}{CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, Dialog _) = RunButtonTestDialog (
+        (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
                                                     Dialog.ButtonAlignments.Right,
@@ -603,13 +629,14 @@ public class DialogTests
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Left
         buttonRow =
             $"{CM.Glyphs.VLine}{CM.Glyphs.LeftBracket} {btnText} {CM.Glyphs.RightBracket}      {CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, Dialog _) = RunButtonTestDialog (
+        (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
                                                     Dialog.ButtonAlignments.Left,
@@ -617,6 +644,7 @@ public class DialogTests
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
     }
 
     [Fact]
@@ -642,7 +670,7 @@ public class DialogTests
 
         d.SetBufferSize (buttonRow.Length, 3);
 
-        (runstate, Dialog _) = RunButtonTestDialog (
+        (runstate, Dialog dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
                                                     Dialog.ButtonAlignments.Center,
@@ -652,12 +680,13 @@ public class DialogTests
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Justify
         buttonRow = $@"{CM.Glyphs.VLine}{btn1}  {btn2}  {btn3}{CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, Dialog _) = RunButtonTestDialog (
+        (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
                                                     Dialog.ButtonAlignments.Justify,
@@ -667,12 +696,13 @@ public class DialogTests
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Right
         buttonRow = $@"{CM.Glyphs.VLine}  {btn1} {btn2} {btn3}{CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, Dialog _) = RunButtonTestDialog (
+        (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
                                                     Dialog.ButtonAlignments.Right,
@@ -682,12 +712,13 @@ public class DialogTests
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Left
         buttonRow = $@"{CM.Glyphs.VLine}{btn1} {btn2} {btn3}  {CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, Dialog _) = RunButtonTestDialog (
+        (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
                                                     Dialog.ButtonAlignments.Left,
@@ -697,6 +728,7 @@ public class DialogTests
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
     }
 
     [Fact]
@@ -720,7 +752,7 @@ public class DialogTests
 
         d.SetBufferSize (buttonRow.Length, 3);
 
-        (runstate, Dialog _) = RunButtonTestDialog (
+        (runstate, Dialog dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
                                                     Dialog.ButtonAlignments.Center,
@@ -729,12 +761,13 @@ public class DialogTests
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Justify
         buttonRow = $@"{CM.Glyphs.VLine}{btn1}   {btn2}{CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, Dialog _) = RunButtonTestDialog (
+        (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
                                                     Dialog.ButtonAlignments.Justify,
@@ -743,12 +776,13 @@ public class DialogTests
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Right
         buttonRow = $@"{CM.Glyphs.VLine}  {btn1} {btn2}{CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, Dialog _) = RunButtonTestDialog (
+        (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
                                                     Dialog.ButtonAlignments.Right,
@@ -757,12 +791,13 @@ public class DialogTests
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Left
         buttonRow = $@"{CM.Glyphs.VLine}{btn1} {btn2}  {CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, Dialog _) = RunButtonTestDialog (
+        (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
                                                     Dialog.ButtonAlignments.Left,
@@ -771,6 +806,7 @@ public class DialogTests
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
     }
 
     [Fact]
@@ -807,6 +843,7 @@ public class DialogTests
         buttonRow = $@"{CM.Glyphs.VLine}         {btn2} {CM.Glyphs.VLine}";
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Justify
         Assert.Equal (width, buttonRow.Length);
@@ -818,6 +855,7 @@ public class DialogTests
         buttonRow = $@"{CM.Glyphs.VLine}          {btn2}{CM.Glyphs.VLine}";
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Right
         Assert.Equal (width, buttonRow.Length);
@@ -828,6 +866,7 @@ public class DialogTests
         RunIteration (ref runstate, ref firstIteration);
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
 
         // Left
         Assert.Equal (width, buttonRow.Length);
@@ -839,6 +878,7 @@ public class DialogTests
         buttonRow = $@"{CM.Glyphs.VLine}        {btn2}  {CM.Glyphs.VLine}";
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
+        dlg.Dispose ();
     }
 
     [Fact]
@@ -1086,7 +1126,7 @@ public class DialogTests
                          }
                      };
 
-        Run ();
+        Run ().Dispose ();
         Shutdown ();
 
         Assert.Equal (4, iterations);
@@ -1101,6 +1141,7 @@ public class DialogTests
             var fd = new FileDialog ();
             fd.Ready += (s, e) => RequestStop ();
             Run (fd);
+            fd.Dispose ();
         }
     }
 
@@ -1157,7 +1198,8 @@ public class DialogTests
     [AutoInitShutdown]
     public void Location_When_Not_Application_Top_Not_Default ()
     {
-        Top.BorderStyle = LineStyle.Double;
+        var top = new Toplevel ();
+        top.BorderStyle = LineStyle.Double;
 
         int iterations = -1;
 
@@ -1168,7 +1210,7 @@ public class DialogTests
                          if (iterations == 0)
                          {
                              var d = new Dialog { X = 5, Y = 5, Height = 3, Width = 5 };
-                             Begin (d);
+                             var rs = Begin (d);
 
                              Assert.Equal (new Point (5, 5), (Point)d.Frame.Location);
 
@@ -1186,9 +1228,11 @@ public class DialogTests
 ╚══════════════════╝",
                                                                            _output
                                                                           );
+                             End (rs);
+                             d.Dispose ();
 
                              d = new Dialog { X = 5, Y = 5 };
-                             Begin (d);
+                             rs = Begin (d);
 
                              // This is because of PostionTopLevels and EnsureVisibleBounds
                              Assert.Equal (new (3, 2), d.Frame.Location);
@@ -1224,6 +1268,8 @@ public class DialogTests
 ╚══════════════════╝",
                                                                            _output
                                                                           );
+                             End (rs);
+                             d.Dispose ();
                          }
                          else if (iterations > 0)
                          {
@@ -1231,9 +1277,8 @@ public class DialogTests
                          }
                      };
 
-        Begin (Top);
         ((FakeDriver)Driver).SetBufferSize (20, 10);
-        Run ();
+        Run (top);
     }
 
     [Fact]
@@ -1331,4 +1376,104 @@ public class DialogTests
 
         return (Begin (dlg), dlg);
     }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void Run_Does_Not_Dispose_Dialog ()
+    {
+        var top = new Toplevel ();
+
+        Dialog dlg = new ();
+
+        dlg.Ready += Dlg_Ready;
+
+        Run (dlg);
+
+#if DEBUG_IDISPOSABLE
+        Assert.False (dlg.WasDisposed);
+        Assert.False (Top.WasDisposed);
+        Assert.NotEqual (top, Top);
+        Assert.Equal (dlg, Top);
+#endif
+
+        // dlg wasn't disposed yet and it's possible to access to his properties
+        Assert.False (dlg.Canceled);
+        Assert.NotNull (dlg);
+
+        dlg.Canceled = true;
+        Assert.True (dlg.Canceled);
+
+        dlg.Dispose ();
+        top.Dispose ();
+#if DEBUG_IDISPOSABLE
+        Assert.True (dlg.WasDisposed);
+        Assert.True (Top.WasDisposed);
+        Assert.NotNull (Top);
+#endif
+        Shutdown();
+        Assert.Null (Top);
+
+        return;
+
+        void Dlg_Ready (object sender, EventArgs e)
+        {
+            RequestStop ();
+        }
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void Can_Access_Cancel_Property_After_Run ()
+    {
+        Dialog dlg = new ();
+
+        dlg.Ready += Dlg_Ready;
+
+        Run (dlg);
+
+#if DEBUG_IDISPOSABLE
+        Assert.False (dlg.WasDisposed);
+        Assert.False (Top.WasDisposed);
+        Assert.Equal (dlg, Top);
+#endif
+
+        Assert.True (dlg.Canceled);
+
+        // Run it again is possible because it isn't disposed yet
+        Run (dlg);
+
+        // Run another view without dispose the prior will throw an assertion
+#if DEBUG_IDISPOSABLE
+        Dialog dlg2 = new ();
+        dlg2.Ready += Dlg_Ready;
+        var exception = Record.Exception (() => Run (dlg2));
+        Assert.NotNull (exception);
+
+        dlg.Dispose();
+        // Now it's possible to tun dlg2 without throw
+        Run (dlg2);
+
+        Assert.True (dlg.WasDisposed);
+        Assert.False (Top.WasDisposed);
+        Assert.Equal (dlg2, Top);
+        Assert.False (dlg2.WasDisposed);
+
+        dlg2.Dispose ();
+        // Now an assertion will throw accessing the Canceled property
+        exception = Record.Exception (() => Assert.True (dlg.Canceled));
+        Assert.NotNull (exception);
+        Assert.True (Top.WasDisposed);
+        Shutdown ();
+        Assert.True (dlg2.WasDisposed);
+        Assert.Null (Top);
+#endif
+
+        return;
+
+        void Dlg_Ready (object sender, EventArgs e)
+        {
+            ((Dialog)sender).Canceled = true;
+            RequestStop ();
+        }
+    }
 }

+ 24 - 39
UnitTests/Dialogs/MessageBoxTests.cs

@@ -12,8 +12,6 @@ public class MessageBoxTests
     [AutoInitShutdown]
     public void KeyBindings_Enter_Causes_Focused_Button_Click ()
     {
-        Application.Begin (Application.Top);
-
         int result = -1;
 
         var iteration = 0;
@@ -43,7 +41,7 @@ public class MessageBoxTests
                                              break;
                                      }
                                  };
-        Application.Run ();
+        Application.Run ().Dispose ();
 
         Assert.Equal (1, result);
     }
@@ -52,8 +50,6 @@ public class MessageBoxTests
     [AutoInitShutdown]
     public void KeyBindings_Esc_Closes ()
     {
-        Application.Begin (Application.Top);
-
         var result = 999;
 
         var iteration = 0;
@@ -81,7 +77,7 @@ public class MessageBoxTests
                                              break;
                                      }
                                  };
-        Application.Run ();
+        Application.Run ().Dispose ();
 
         Assert.Equal (-1, result);
     }
@@ -90,8 +86,6 @@ public class MessageBoxTests
     [AutoInitShutdown]
     public void KeyBindings_Space_Causes_Focused_Button_Click ()
     {
-        Application.Begin (Application.Top);
-
         int result = -1;
 
         var iteration = 0;
@@ -121,7 +115,7 @@ public class MessageBoxTests
                                              break;
                                      }
                                  };
-        Application.Run ();
+        Application.Run ().Dispose ();
 
         Assert.Equal (1, result);
     }
@@ -131,7 +125,6 @@ public class MessageBoxTests
     public void Location_Default ()
     {
         int iterations = -1;
-        Application.Begin (Application.Top);
         ((FakeDriver)Application.Driver).SetBufferSize (100, 100);
 
         Application.Iteration += (s, a) =>
@@ -159,7 +152,7 @@ public class MessageBoxTests
                                      }
                                  };
 
-        Application.Run ();
+        Application.Run ().Dispose ();
     }
 
     [Theory]
@@ -179,7 +172,6 @@ public class MessageBoxTests
     )
     {
         int iterations = -1;
-        Application.Begin (Application.Top);
 
         Application.Iteration += (s, a) =>
                                  {
@@ -241,7 +233,7 @@ public class MessageBoxTests
                                      }
                                  };
 
-        Application.Run ();
+        Application.Run ().Dispose ();
     }
 
     [Fact]
@@ -249,8 +241,8 @@ public class MessageBoxTests
     public void Message_Long_Without_Spaces_WrapMessage_True ()
     {
         int iterations = -1;
-        Application.Begin (Application.Top);
-        Application.Top.BorderStyle = LineStyle.Double;
+        var top = new Toplevel ();
+        top.BorderStyle = LineStyle.Double;
         ((FakeDriver)Application.Driver).SetBufferSize (20, 10);
 
         var btn =
@@ -325,7 +317,7 @@ public class MessageBoxTests
                                      }
                                  };
 
-        Application.Run ();
+        Application.Run (top);
     }
 
     [Fact]
@@ -333,8 +325,8 @@ public class MessageBoxTests
     public void Message_With_Spaces_WrapMessage_False ()
     {
         int iterations = -1;
-        Application.Begin (Application.Top);
-        Application.Top.BorderStyle = LineStyle.Double;
+        var top = new Toplevel ();
+        top.BorderStyle = LineStyle.Double;
         ((FakeDriver)Application.Driver).SetBufferSize (20, 10);
 
         var btn =
@@ -410,7 +402,7 @@ ffffffffffffffffffff
                                      }
                                  };
 
-        Application.Run ();
+        Application.Run (top);
     }
 
     [Fact]
@@ -418,8 +410,8 @@ ffffffffffffffffffff
     public void Message_With_Spaces_WrapMessage_True ()
     {
         int iterations = -1;
-        Application.Begin (Application.Top);
-        Application.Top.BorderStyle = LineStyle.Double;
+        var top = new Toplevel();
+        top.BorderStyle = LineStyle.Double;
         ((FakeDriver)Application.Driver).SetBufferSize (20, 10);
 
         var btn =
@@ -499,7 +491,7 @@ ffffffffffffffffffff
                                      }
                                  };
 
-        Application.Run ();
+        Application.Run (top);
     }
 
     [Fact]
@@ -507,8 +499,8 @@ ffffffffffffffffffff
     public void Message_Without_Spaces_WrapMessage_False ()
     {
         int iterations = -1;
-        Application.Begin (Application.Top);
-        Application.Top.BorderStyle = LineStyle.Double;
+        var top = new Toplevel();
+        top.BorderStyle = LineStyle.Double;
         ((FakeDriver)Application.Driver).SetBufferSize (20, 10);
 
         var btn =
@@ -579,7 +571,7 @@ ffffffffffffffffffff
                                      }
                                  };
 
-        Application.Run ();
+        Application.Run (top);
     }
 
     [Fact]
@@ -587,7 +579,6 @@ ffffffffffffffffffff
     public void Size_Default ()
     {
         int iterations = -1;
-        Application.Begin (Application.Top);
         ((FakeDriver)Application.Driver).SetBufferSize (100, 100);
 
         Application.Iteration += (s, a) =>
@@ -613,7 +604,7 @@ ffffffffffffffffffff
                                      }
                                  };
 
-        Application.Run ();
+        Application.Run ().Dispose ();
     }
 
     [Fact]
@@ -621,7 +612,6 @@ ffffffffffffffffffff
     public void Size_JustBigEnough_Fixed_Size ()
     {
         int iterations = -1;
-        Application.Begin (Application.Top);
 
         var btn =
             $"{
@@ -665,16 +655,16 @@ ffffffffffffffffffff
                                      }
                                  };
 
-        Application.Run ();
+        Application.Run ().Dispose ();
     }
 
     [Fact]
     [AutoInitShutdown]
     public void Size_No_With_Button ()
     {
-        Application.Top.BorderStyle = LineStyle.Double;
+        var top = new Toplevel ();
+        top.BorderStyle = LineStyle.Double;
         int iterations = -1;
-        Application.Begin (Application.Top);
 
         var aboutMessage = new StringBuilder ();
         aboutMessage.AppendLine (@"0123456789012345678901234567890123456789");
@@ -728,7 +718,7 @@ ffffffffffffffffffff
                                      }
                                  };
 
-        Application.Run ();
+        Application.Run (top);
     }
 
     [Fact]
@@ -736,7 +726,6 @@ ffffffffffffffffffff
     public void Size_None_No_Buttons ()
     {
         int iterations = -1;
-        Application.Begin (Application.Top);
 
         Application.Iteration += (s, a) =>
                                  {
@@ -767,7 +756,7 @@ ffffffffffffffffffff
                                      }
                                  };
 
-        Application.Run ();
+        Application.Run ().Dispose ();
     }
 
     [Theory]
@@ -783,7 +772,6 @@ ffffffffffffffffffff
     public void Size_Not_Default_Message (int height, int width, string message)
     {
         int iterations = -1;
-        Application.Begin (Application.Top);
         ((FakeDriver)Application.Driver).SetBufferSize (100, 100);
 
         Application.Iteration += (s, a) =>
@@ -821,7 +809,6 @@ ffffffffffffffffffff
     public void Size_Not_Default_Message_Button (int height, int width, string message)
     {
         int iterations = -1;
-        Application.Begin (Application.Top);
         ((FakeDriver)Application.Driver).SetBufferSize (100, 100);
 
         Application.Iteration += (s, a) =>
@@ -855,7 +842,6 @@ ffffffffffffffffffff
     public void Size_Not_Default_No_Message (int height, int width)
     {
         int iterations = -1;
-        Application.Begin (Application.Top);
         ((FakeDriver)Application.Driver).SetBufferSize (100, 100);
 
         Application.Iteration += (s, a) =>
@@ -885,7 +871,6 @@ ffffffffffffffffffff
     public void Size_Tiny_Fixed_Size ()
     {
         int iterations = -1;
-        Application.Begin (Application.Top);
 
         Application.Iteration += (s, a) =>
                                  {
@@ -920,6 +905,6 @@ ffffffffffffffffffff
                                      }
                                  };
 
-        Application.Run ();
+        Application.Run ().Dispose ();
     }
 }

+ 3 - 2
UnitTests/Drawing/LineCanvasTests.cs

@@ -1305,8 +1305,9 @@ public class LineCanvasTests
     private View GetCanvas (out LineCanvas canvas, int offsetX = 0, int offsetY = 0)
     {
         var v = new View { Width = 10, Height = 5, Bounds = new Rectangle (0, 0, 10, 5) };
-        Application.Top.Add (v);
-        Application.Begin (Application.Top);
+        var top = new Toplevel ();
+        top.Add (v);
+        Application.Begin (top);
 
         LineCanvas canvasCopy = canvas = new LineCanvas ();
 

+ 12 - 8
UnitTests/Drawing/RulerTests.cs

@@ -48,8 +48,9 @@ public class RulerTests
 
         // Add a frame so we can see the ruler
         var f = new FrameView { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill () };
-        Application.Top.Add (f);
-        Application.Begin (Application.Top);
+        var top = new Toplevel ();
+        top.Add (f);
+        Application.Begin (top);
         ((FakeDriver)Application.Driver).SetBufferSize (len + 5, 5);
         Assert.Equal (new Rectangle (0, 0, len + 5, 5), f.Frame);
 
@@ -120,8 +121,9 @@ public class RulerTests
 
         // Add a frame so we can see the ruler
         var f = new FrameView { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill () };
-        Application.Top.Add (f);
-        Application.Begin (Application.Top);
+        var top = new Toplevel ();
+        top.Add (f);
+        Application.Begin (top);
         ((FakeDriver)Application.Driver).SetBufferSize (len + 5, 5);
         Assert.Equal (new Rectangle (0, 0, len + 5, 5), f.Frame);
 
@@ -165,8 +167,9 @@ public class RulerTests
         // Add a frame so we can see the ruler
         var f = new FrameView { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill () };
 
-        Application.Top.Add (f);
-        Application.Begin (Application.Top);
+        var top = new Toplevel ();
+        top.Add (f);
+        Application.Begin (top);
         ((FakeDriver)Application.Driver).SetBufferSize (5, len + 5);
         Assert.Equal (new Rectangle (0, 0, 5, len + 5), f.Frame);
 
@@ -297,8 +300,9 @@ public class RulerTests
         // Add a frame so we can see the ruler
         var f = new FrameView { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill () };
 
-        Application.Top.Add (f);
-        Application.Begin (Application.Top);
+        var top = new Toplevel ();
+        top.Add (f);
+        Application.Begin (top);
         ((FakeDriver)Application.Driver).SetBufferSize (5, len + 5);
         Assert.Equal (new Rectangle (0, 0, 5, len + 5), f.Frame);
 

+ 3 - 2
UnitTests/Drawing/ThicknessTests.cs

@@ -177,8 +177,9 @@ public class ThicknessTests
         // Add a frame so we can see the ruler
         var f = new FrameView { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill () };
 
-        Application.Top.Add (f);
-        Application.Begin (Application.Top);
+        var top = new Toplevel ();
+        top.Add (f);
+        Application.Begin (top);
 
         ((FakeDriver)Application.Driver).SetBufferSize (45, 20);
         var t = new Thickness (0, 0, 0, 0);

+ 4 - 3
UnitTests/Input/EscSeqUtilsTests.cs

@@ -695,8 +695,9 @@ public class EscSeqUtilsTests
         Assert.False (_isReq);
 
         var view = new View { Width = Dim.Fill (), Height = Dim.Fill (), WantContinuousButtonPressed = true };
-        Application.Top.Add (view);
-        Application.Begin (Application.Top);
+        var top = new Toplevel ();
+        top.Add (view);
+        Application.Begin (top);
 
         Application.OnMouseEvent (
                                   new MouseEventEventArgs (
@@ -769,7 +770,7 @@ public class EscSeqUtilsTests
                                      }
                                  };
 
-        Application.Run ();
+        Application.Run (top);
 
         Assert.Null (Application.WantContinuousButtonPressedView);
 

+ 3 - 1
UnitTests/TestHelpers.cs

@@ -74,6 +74,7 @@ public class AutoInitShutdownAttribute : BeforeAfterTestAttribute
 
         if (AutoInit)
         {
+            Application.Top?.Dispose ();
             Application.Shutdown ();
 #if DEBUG_IDISPOSABLE
             if (Responder.Instances.Count == 0)
@@ -94,8 +95,9 @@ public class AutoInitShutdownAttribute : BeforeAfterTestAttribute
 
         if (AutoInit)
         {
-#if DEBUG_IDISPOSABLE
+            ConfigurationManager.Reset ();
 
+#if DEBUG_IDISPOSABLE
             // Clear out any lingering Responder instances from previous tests
             if (Responder.Instances.Count == 0)
             {

+ 2 - 2
UnitTests/Text/AutocompleteTests.cs

@@ -20,7 +20,7 @@ public class AutocompleteTests
                                 .Select (s => s.Value)
                                 .Distinct ()
                                 .ToList ();
-        Toplevel top = Application.Top;
+        Toplevel top = new ();
         top.Add (tv);
         Application.Begin (top);
 
@@ -152,7 +152,7 @@ This an long line and against TextView.",
     public void KeyBindings_Command ()
     {
         var tv = new TextView { Width = 10, Height = 2, Text = " Fortunately super feature." };
-        Toplevel top = Application.Top;
+        Toplevel top = new ();
         top.Add (tv);
         Application.Begin (top);
 

+ 29 - 33
UnitTests/UICatalog/ScenarioTests.cs

@@ -40,7 +40,7 @@ public class ScenarioTests
             FakeConsole.PushMockKeyPress ((KeyCode)Application.QuitKey);
 
             // The only key we care about is the QuitKey
-            Application.Top.KeyDown += (sender, args) =>
+            Application.KeyDown += (sender, args) =>
                                        {
                                            _output.WriteLine ($"  Keypress: {args.KeyCode}");
 
@@ -53,29 +53,28 @@ public class ScenarioTests
             uint abortTime = 500;
 
             // If the scenario doesn't close within 500ms, this will force it to quit
-            Func<bool> forceCloseCallback = () =>
-                                            {
-                                                if (Application.Top.Running && FakeConsole.MockKeyPresses.Count == 0)
-                                                {
-                                                    Application.RequestStop ();
-
-                                                    // See #2474 for why this is commented out
-                                                    Assert.Fail (
-                                                                 $"'{
-                                                                     scenario.GetName ()
-                                                                 }' failed to Quit with {
-                                                                     Application.QuitKey
-                                                                 } after {
-                                                                     abortTime
-                                                                 }ms. Force quit."
-                                                                );
-                                                }
-
-                                                return false;
-                                            };
+            bool ForceCloseCallback ()
+            {
+                if (Application.Top.Running && FakeConsole.MockKeyPresses.Count == 0)
+                {
+                    Application.RequestStop ();
+
+                    // See #2474 for why this is commented out
+                    Assert.Fail (
+                                 $"'{
+                                     scenario.GetName ()
+                                 }' failed to Quit with {
+                                     Application.QuitKey
+                                 } after {
+                                     abortTime
+                                 }ms. Force quit.");
+                }
+
+                return false;
+            }
 
             //output.WriteLine ($"  Add timeout to force quit after {abortTime}ms");
-            _ = Application.AddTimeout (TimeSpan.FromMilliseconds (abortTime), forceCloseCallback);
+            _ = Application.AddTimeout (TimeSpan.FromMilliseconds (abortTime), ForceCloseCallback);
 
             Application.Iteration += (s, a) =>
                                      {
@@ -87,9 +86,7 @@ public class ScenarioTests
                                          }
                                      };
 
-            scenario.Init ();
-            scenario.Setup ();
-            scenario.Run ();
+            scenario.Main ();
             scenario.Dispose ();
 
             Application.Shutdown ();
@@ -135,7 +132,7 @@ public class ScenarioTests
 
         Application.Init (new FakeDriver ());
 
-        Toplevel Top = Application.Top;
+        Toplevel top = new Toplevel ();
 
         _viewClasses = GetAllViewClassesCollection ()
                        .OrderBy (t => t.Name)
@@ -321,9 +318,9 @@ public class ScenarioTests
 
         _hRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView);
 
-        Top.Add (_leftPane, _settingsPane, _hostPane);
+        top.Add (_leftPane, _settingsPane, _hostPane);
 
-        Top.LayoutSubviews ();
+        top.LayoutSubviews ();
 
         _curView = CreateClass (_viewClasses.First ().Value);
 
@@ -348,10 +345,11 @@ public class ScenarioTests
                                      }
                                  };
 
-        Application.Run ();
+        Application.Run (top);
 
         Assert.Equal (_viewClasses.Count, iterations);
 
+        top.Dispose ();
         Application.Shutdown ();
 
         void DimPosChanged (View view)
@@ -624,15 +622,13 @@ public class ScenarioTests
                                      }
                                  };
 
-        Application.Top.KeyDown += (sender, args) =>
+        Application.KeyDown += (sender, args) =>
                                    {
                                        // See #2474 for why this is commented out
                                        Assert.Equal (KeyCode.CtrlMask | KeyCode.Q, args.KeyCode);
                                    };
 
-        generic.Init ();
-        generic.Setup ();
-        generic.Run ();
+        generic.Main ();
 
         Assert.Equal (0, abortCount);
 

+ 8 - 6
UnitTests/View/Adornment/BorderTests.cs

@@ -711,12 +711,13 @@ public class BorderTests
     [AutoInitShutdown]
     public void HasSuperView ()
     {
-        Application.Top.BorderStyle = LineStyle.Double;
+        var top = new Toplevel ();
+        top.BorderStyle = LineStyle.Double;
 
         var frame = new FrameView { Width = Dim.Fill (), Height = Dim.Fill () };
 
-        Application.Top.Add (frame);
-        RunState rs = Application.Begin (Application.Top);
+        top.Add (frame);
+        RunState rs = Application.Begin (top);
         var firstIteration = false;
 
         ((FakeDriver)Application.Driver).SetBufferSize (5, 5);
@@ -737,12 +738,13 @@ public class BorderTests
     [AutoInitShutdown]
     public void HasSuperView_Title ()
     {
-        Application.Top.BorderStyle = LineStyle.Double;
+        var top = new Toplevel ();
+        top.BorderStyle = LineStyle.Double;
 
         var frame = new FrameView { Title = "1234", Width = Dim.Fill (), Height = Dim.Fill () };
 
-        Application.Top.Add (frame);
-        RunState rs = Application.Begin (Application.Top);
+        top.Add (frame);
+        RunState rs = Application.Begin (top);
         var firstIteration = false;
 
         ((FakeDriver)Application.Driver).SetBufferSize (10, 4);

+ 13 - 9
UnitTests/View/DrawTests.cs

@@ -70,7 +70,7 @@ public class DrawTests
         var view = new View { Text = r.ToString (), Height = Dim.Fill (), Width = Dim.Fill () };
         var tf = new TextField { Text = us, Y = 1, Width = 3 };
         win.Add (view, tf);
-        Toplevel top = Application.Top;
+        Toplevel top = new ();
         top.Add (win);
 
         Application.Begin (top);
@@ -135,7 +135,8 @@ public class DrawTests
         };
         var win = new Window { Width = Dim.Fill (), Height = Dim.Fill () };
         win.Add (tv);
-        Application.Top.Add (win);
+        var top = new Toplevel ();
+        top.Add (win);
 
         // Don't use Label. It sets AutoSize = true which is not what we're testing here.
         var view = new View { Text = "ワイドルーン。", Height = Dim.Fill (), Width = Dim.Fill () };
@@ -143,8 +144,8 @@ public class DrawTests
         // Don't have unit tests use things that aren't absolutely critical for the test, like Dialog
         var dg = new Window { X = 2, Y = 2, Width = 14, Height = 3 };
         dg.Add (view);
-        Application.Begin (Application.Top);
-        Application.Begin (dg);
+        RunState rsTop = Application.Begin (top);
+        RunState rsDiag = Application.Begin (dg);
         ((FakeDriver)Application.Driver).SetBufferSize (30, 10);
 
         const string expectedOutput = """
@@ -163,6 +164,9 @@ public class DrawTests
 
         Rectangle pos = TestHelpers.AssertDriverContentsWithFrameAre (expectedOutput, _output);
         Assert.Equal (new Rectangle (0, 0, 30, 10), pos);
+
+        Application.End (rsDiag);
+        Application.End (rsTop);
     }
 
     [Fact]
@@ -189,7 +193,7 @@ public class DrawTests
             VerticalTextAlignment = VerticalTextAlignment.Bottom,
             ColorScheme = Colors.ColorSchemes ["Base"]
         };
-        Toplevel top = Application.Top;
+        Toplevel top = new ();
         top.Add (viewRight, viewBottom);
 
         Application.Begin (top);
@@ -402,7 +406,7 @@ public class DrawTests
             Height = 5
         };
         container.Add (content);
-        Toplevel top = Application.Top;
+        Toplevel top = new ();
         top.Add (container);
         Application.Driver.Clip = container.Frame;
         Application.Begin (top);
@@ -517,7 +521,7 @@ public class DrawTests
             Height = 5
         };
         container.Add (content);
-        Toplevel top = Application.Top;
+        Toplevel top = new ();
         top.Add (container);
 
         // BUGBUG: v2 - it's bogus to reference .Frame before BeginInit. And why is the clip being set anyway???
@@ -604,7 +608,7 @@ public class DrawTests
             Height = 5
         };
         container.Add (content);
-        Toplevel top = Application.Top;
+        Toplevel top = new ();
         top.Add (container);
         Application.Driver.Clip = container.Frame;
         Application.Begin (top);
@@ -723,7 +727,7 @@ public class DrawTests
         var view = new Label { Text = r.ToString () };
         var tf = new TextField { Text = us, Y = 1, Width = 3 };
         win.Add (view, tf);
-        Toplevel top = Application.Top;
+        Toplevel top = new ();
         top.Add (win);
 
         Application.Begin (top);

+ 9 - 8
UnitTests/View/Layout/DimTests.cs

@@ -28,7 +28,7 @@ public class DimTests
     [AutoInitShutdown]
     public void Dim_Add_Operator ()
     {
-        Toplevel top = Application.Top;
+        Toplevel top = new ();
 
         var view = new View { X = 0, Y = 0, Width = 20, Height = 0 };
         var field = new TextField { X = 0, Y = Pos.Bottom (view), Width = 20 };
@@ -102,7 +102,7 @@ public class DimTests
     [AutoInitShutdown]
     public void Dim_Subtract_Operator ()
     {
-        Toplevel top = Application.Top;
+        Toplevel top = new ();
 
         var view = new View { X = 0, Y = 0, Width = 20, Height = 0 };
         var field = new TextField { X = 0, Y = Pos.Bottom (view), Width = 20 };
@@ -386,10 +386,11 @@ public class DimTests
         };
 
         container.Add (label);
-        Application.Top.Add (container);
-        Application.Top.BeginInit ();
-        Application.Top.EndInit ();
-        Application.Top.LayoutSubviews ();
+        var top = new Toplevel ();
+        top.Add (container);
+        top.BeginInit ();
+        top.EndInit ();
+        top.LayoutSubviews ();
 
         Assert.Equal (100, container.Frame.Width);
         Assert.Equal (100, container.Frame.Height);
@@ -528,7 +529,7 @@ public class DimTests
     public void Only_DimAbsolute_And_DimFactor_As_A_Different_Procedure_For_Assigning_Value_To_Width_Or_Height ()
     {
         // Testing with the Button because it properly handles the Dim class.
-        Toplevel t = Application.Top;
+        Toplevel t = new ();
 
         var w = new Window { Width = 100, Height = 100 };
 
@@ -765,7 +766,7 @@ public class DimTests
 
         Application.Iteration += (s, a) => Application.RequestStop ();
 
-        Application.Run ();
+        Application.Run (t);
     }
 
     [Fact]

+ 24 - 15
UnitTests/View/Layout/LayoutTests.cs

@@ -331,12 +331,13 @@ public class LayoutTests
     public void DimFill_SizedCorrectly ()
     {
         var view = new View { Width = Dim.Fill (), Height = Dim.Fill (), BorderStyle = LineStyle.Single };
-        Application.Top.Add (view);
-        RunState rs = Application.Begin (Application.Top);
+        var top = new Toplevel ();
+        top.Add (view);
+        RunState rs = Application.Begin (top);
         ((FakeDriver)Application.Driver).SetBufferSize (32, 5);
 
         //view.SetNeedsLayout ();
-        Application.Top.LayoutSubviews ();
+        top.LayoutSubviews ();
 
         //view.SetRelativeLayout (new (0, 0, 32, 5));
         Assert.Equal (32, view.Frame.Width);
@@ -349,7 +350,7 @@ public class LayoutTests
     {
         Application.Init (new FakeDriver ());
 
-        Toplevel top = Application.Top;
+        Toplevel top = new ();
 
         var view = new View { X = -2, Text = "view" };
         top.Add (view);
@@ -363,7 +364,7 @@ public class LayoutTests
 
         try
         {
-            Application.Run ();
+            Application.Run (top);
         }
         catch (IndexOutOfRangeException ex)
         {
@@ -371,6 +372,7 @@ public class LayoutTests
             Assert.IsType<IndexOutOfRangeException> (ex);
         }
 
+        top.Dispose ();
         // Shutdown must be called to safely clean up Application if Init has been called
         Application.Shutdown ();
     }
@@ -381,7 +383,7 @@ public class LayoutTests
     {
         Application.Init (new FakeDriver ());
 
-        Toplevel top = Application.Top;
+        Toplevel top = new ();
 
         var view = new View { Y = -2, Height = 10, TextDirection = TextDirection.TopBottom_LeftRight, Text = "view" };
         top.Add (view);
@@ -395,7 +397,7 @@ public class LayoutTests
 
         try
         {
-            Application.Run ();
+            Application.Run (top);
         }
         catch (IndexOutOfRangeException ex)
         {
@@ -403,6 +405,7 @@ public class LayoutTests
             Assert.IsType<IndexOutOfRangeException> (ex);
         }
 
+        top.Dispose ();
         // Shutdown must be called to safely clean up Application if Init has been called
         Application.Shutdown ();
     }
@@ -488,7 +491,7 @@ public class LayoutTests
     {
         Application.Init (new FakeDriver ());
 
-        Toplevel t = Application.Top;
+        Toplevel t = new ();
 
         var w = new Window { X = Pos.Left (t) + 2, Y = Pos.At (2) };
 
@@ -506,7 +509,8 @@ public class LayoutTests
 
         Application.Iteration += (s, a) => Application.RequestStop ();
 
-        Application.Run ();
+        Application.Run (t);
+        t.Dispose ();
         Application.Shutdown ();
     }
 
@@ -545,14 +549,16 @@ public class LayoutTests
     {
         Application.Init (new FakeDriver ());
 
-        var w = new Window { X = Pos.Left (Application.Top) + 2, Y = Pos.Top (Application.Top) + 2 };
+        var top = new Toplevel ();
+        var w = new Window { X = Pos.Left (top) + 2, Y = Pos.Top (top) + 2 };
         var f = new FrameView ();
         var v1 = new View { X = Pos.Left (w) + 2, Y = Pos.Top (w) + 2 };
         var v2 = new View { X = Pos.Left (v1) + 2, Y = Pos.Top (v1) + 2 };
 
         f.Add (v1, v2);
         w.Add (f);
-        Application.Top.Add (w);
+        top.Add (w);
+        Application.Begin (top);
 
         f.X = Pos.X (Application.Top) + Pos.X (v2) - Pos.X (v1);
         f.Y = Pos.Y (Application.Top) + Pos.Y (v2) - Pos.Y (v1);
@@ -574,6 +580,7 @@ public class LayoutTests
         Application.Iteration += (s, a) => Application.RequestStop ();
 
         Assert.Throws<InvalidOperationException> (() => Application.Run ());
+        top.Dispose ();
         Application.Shutdown ();
     }
 
@@ -634,8 +641,9 @@ public class LayoutTests
         Assert.Equal (10, rHeight);
         Assert.False (v.IsInitialized);
 
-        Application.Top.Add (top);
-        Application.Begin (Application.Top);
+        var toplevel = new Toplevel ();
+        toplevel.Add (top);
+        Application.Begin (toplevel);
 
         Assert.True (v.IsInitialized);
 
@@ -665,8 +673,9 @@ public class LayoutTests
         Assert.Equal (70, rWidth);
         Assert.False (v.IsInitialized);
 
-        Application.Top.Add (top);
-        Application.Begin (Application.Top);
+        var toplevel = new Toplevel ();
+        toplevel.Add (top);
+        Application.Begin (toplevel);
 
         Assert.True (v.IsInitialized);
         v.Width = 75;

+ 27 - 20
UnitTests/View/Layout/PosTests.cs

@@ -43,7 +43,7 @@ public class PosTests
 
         win.Add (tv);
 
-        Toplevel top = Application.Top;
+        Toplevel top = new ();
         top.Add (win);
         RunState rs = Application.Begin (top);
 
@@ -73,7 +73,7 @@ public class PosTests
 
         var menu = new MenuBar ();
         var status = new StatusBar ();
-        Toplevel top = Application.Top;
+        Toplevel top = new ();
         top.Add (win, menu, status);
         RunState rs = Application.Begin (top);
 
@@ -317,17 +317,18 @@ public class PosTests
     public void LeftTopBottomRight_Win_ShouldNotThrow ()
     {
         // Setup Fake driver
-        (Window win, Button button) setup ()
+        (Toplevel top, Window win, Button button) setup ()
         {
             Application.Init (new FakeDriver ());
             Application.Iteration += (s, a) => { Application.RequestStop (); };
             var win = new Window { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill () };
-            Application.Top.Add (win);
+            var top = new Toplevel ();
+            top.Add (win);
 
             var button = new Button { X = Pos.Center (), Text = "button" };
             win.Add (button);
 
-            return (win, button);
+            return (top, win, button);
         }
 
         RunState rs;
@@ -337,14 +338,15 @@ public class PosTests
             // Cleanup
             Application.End (rs);
 
+            Application.Top.Dispose ();
             // Shutdown must be called to safely clean up Application if Init has been called
             Application.Shutdown ();
         }
 
         // Test cases:
-        (Window win, Button button) app = setup ();
+        (Toplevel top, Window win, Button button) app = setup ();
         app.button.Y = Pos.Left (app.win);
-        rs = Application.Begin (Application.Top);
+        rs = Application.Begin (app.top);
 
         // If Application.RunState is used then we must use Application.RunLoop with the rs parameter
         Application.RunLoop (rs);
@@ -352,7 +354,7 @@ public class PosTests
 
         app = setup ();
         app.button.Y = Pos.X (app.win);
-        rs = Application.Begin (Application.Top);
+        rs = Application.Begin (app.top);
 
         // If Application.RunState is used then we must use Application.RunLoop with the rs parameter
         Application.RunLoop (rs);
@@ -360,7 +362,7 @@ public class PosTests
 
         app = setup ();
         app.button.Y = Pos.Top (app.win);
-        rs = Application.Begin (Application.Top);
+        rs = Application.Begin (app.top);
 
         // If Application.RunState is used then we must use Application.RunLoop with the rs parameter
         Application.RunLoop (rs);
@@ -368,7 +370,7 @@ public class PosTests
 
         app = setup ();
         app.button.Y = Pos.Y (app.win);
-        rs = Application.Begin (Application.Top);
+        rs = Application.Begin (app.top);
 
         // If Application.RunState is used then we must use Application.RunLoop with the rs parameter
         Application.RunLoop (rs);
@@ -376,7 +378,7 @@ public class PosTests
 
         app = setup ();
         app.button.Y = Pos.Bottom (app.win);
-        rs = Application.Begin (Application.Top);
+        rs = Application.Begin (app.top);
 
         // If Application.RunState is used then we must use Application.RunLoop with the rs parameter
         Application.RunLoop (rs);
@@ -384,7 +386,7 @@ public class PosTests
 
         app = setup ();
         app.button.Y = Pos.Right (app.win);
-        rs = Application.Begin (Application.Top);
+        rs = Application.Begin (app.top);
 
         // If Application.RunState is used then we must use Application.RunLoop with the rs parameter
         Application.RunLoop (rs);
@@ -467,7 +469,7 @@ public class PosTests
     {
         Application.Init (new FakeDriver ());
 
-        Toplevel top = Application.Top;
+        Toplevel top = new ();
 
         var view = new View { X = 0, Y = 0, Width = 20, Height = 20 };
         var field = new TextField { X = 0, Y = 0, Width = 20 };
@@ -510,6 +512,7 @@ public class PosTests
 
         Assert.Equal (20, count);
 
+        top.Dispose ();
         // Shutdown must be called to safely clean up Application if Init has been called
         Application.Shutdown ();
     }
@@ -522,7 +525,7 @@ public class PosTests
     {
         Application.Init (new FakeDriver ());
 
-        Toplevel top = Application.Top;
+        Toplevel top = new ();
 
         var view = new View { X = 0, Y = 0, Width = 20, Height = 20 };
         var field = new TextField { X = 0, Y = 0, Width = 20 };
@@ -578,6 +581,7 @@ public class PosTests
 
         Assert.Equal (0, count);
 
+        top.Dispose ();
         // Shutdown must be called to safely clean up Application if Init has been called
         Application.Shutdown ();
     }
@@ -589,7 +593,7 @@ public class PosTests
     {
         Application.Init (new FakeDriver ());
 
-        Toplevel t = Application.Top;
+        Toplevel t = new ();
 
         var w = new Window { X = 1, Y = 2, Width = 3, Height = 5 };
         t.Add (w);
@@ -602,7 +606,8 @@ public class PosTests
 
         Application.Iteration += (s, a) => Application.RequestStop ();
 
-        Application.Run ();
+        Application.Run (t);
+        t.Dispose ();
         Application.Shutdown ();
     }
 
@@ -637,7 +642,7 @@ public class PosTests
     {
         Application.Init (new FakeDriver ());
 
-        Toplevel t = Application.Top;
+        Toplevel t = new ();
 
         var w = new Window { X = Pos.Left (t) + 2, Y = Pos.Top (t) + 2 };
         var f = new FrameView ();
@@ -651,7 +656,8 @@ public class PosTests
         f.X = Pos.X (v2) - Pos.X (v1);
         f.Y = Pos.Y (v2) - Pos.Y (v1);
 
-        Assert.Throws<InvalidOperationException> (() => Application.Run ());
+        Assert.Throws<InvalidOperationException> (() => Application.Run (t));
+        t.Dispose ();
         Application.Shutdown ();
 
         v2.Dispose ();
@@ -676,8 +682,9 @@ public class PosTests
         };
 
         container.Add (view);
-        Application.Top.Add (container);
-        Application.Top.LayoutSubviews ();
+        var top = new Toplevel ();
+        top.Add (container);
+        top.LayoutSubviews ();
 
         Assert.Equal (100, container.Frame.Width);
         Assert.Equal (100, container.Frame.Height);

+ 3 - 1
UnitTests/View/MouseTests.cs

@@ -68,7 +68,9 @@ public class MouseTests (ITestOutputHelper output)
         testView.Border.Thickness = new (borderThickness);
         testView.Padding.Thickness = new (paddingThickness);
 
-        Application.Top.Add (testView);
+        var top = new Toplevel ();
+        top.Add (testView);
+        Application.Begin (top);
 
         Assert.Equal (new Point (4, 4), testView.Frame.Location);
         Application.OnMouseEvent (new (new () { X = xy, Y = xy, Flags = MouseFlags.Button1Pressed }));

部分文件因文件數量過多而無法顯示