2
0
Эх сурвалжийг харах

Merge pull request #2167 from tig/fixes_520_toplevels_not_cleanedup

Fixes #520. Application.Init / Shutdown are confused about TopLevels created by Application.Begin and not cleaned up by Application.End
Tig 2 жил өмнө
parent
commit
b6dceb3048
54 өөрчлөгдсөн 1906 нэмэгдсэн , 1577 устгасан
  1. 204 86
      Terminal.Gui/Core/Application.cs
  2. 2 2
      Terminal.Gui/Core/MainLoop.cs
  3. 4 4
      Terminal.Gui/Core/Toplevel.cs
  4. 1 1
      Terminal.Gui/Core/View.cs
  5. 1 1
      Terminal.Gui/Windows/Wizard.cs
  6. 4 0
      UICatalog/Properties/launchSettings.json
  7. 8 14
      UICatalog/Scenario.cs
  8. 10 24
      UICatalog/Scenarios/AllViewsTester.cs
  9. 3 6
      UICatalog/Scenarios/BackgroundWorkerCollection.cs
  10. 4 6
      UICatalog/Scenarios/BordersComparisons.cs
  11. 2 2
      UICatalog/Scenarios/Buttons.cs
  12. 2 2
      UICatalog/Scenarios/ClassExplorer.cs
  13. 4 7
      UICatalog/Scenarios/Clipping.cs
  14. 8 9
      UICatalog/Scenarios/CollectionNavigatorTester.cs
  15. 2 2
      UICatalog/Scenarios/ComputedLayout.cs
  16. 1 1
      UICatalog/Scenarios/ContextMenus.cs
  17. 3 3
      UICatalog/Scenarios/CsvEditor.cs
  18. 2 2
      UICatalog/Scenarios/Dialogs.cs
  19. 2 3
      UICatalog/Scenarios/DynamicMenuBar.cs
  20. 2 3
      UICatalog/Scenarios/DynamicStatusBar.cs
  21. 7 7
      UICatalog/Scenarios/Editor.cs
  22. 3 3
      UICatalog/Scenarios/GraphViewExample.cs
  23. 2 2
      UICatalog/Scenarios/HexEditor.cs
  24. 3 3
      UICatalog/Scenarios/InteractiveTree.cs
  25. 6 7
      UICatalog/Scenarios/Keys.cs
  26. 3 3
      UICatalog/Scenarios/LabelsAsButtons.cs
  27. 3 3
      UICatalog/Scenarios/LineViewExample.cs
  28. 2 2
      UICatalog/Scenarios/MessageBoxes.cs
  29. 3 3
      UICatalog/Scenarios/MultiColouredTable.cs
  30. 5 6
      UICatalog/Scenarios/Notepad.cs
  31. 2 2
      UICatalog/Scenarios/ProgressBarStyles.cs
  32. 1 1
      UICatalog/Scenarios/RuneWidthGreaterThanOne.cs
  33. 4 4
      UICatalog/Scenarios/Scrolling.cs
  34. 2 2
      UICatalog/Scenarios/SingleBackgroundWorker.cs
  35. 3 3
      UICatalog/Scenarios/SyntaxHighlighting.cs
  36. 3 7
      UICatalog/Scenarios/TabViewExample.cs
  37. 4 4
      UICatalog/Scenarios/TableEditor.cs
  38. 1 1
      UICatalog/Scenarios/TextFormatterDemo.cs
  39. 2 2
      UICatalog/Scenarios/TextViewAutocompletePopup.cs
  40. 2 2
      UICatalog/Scenarios/Threading.cs
  41. 3 3
      UICatalog/Scenarios/TreeUseCases.cs
  42. 3 3
      UICatalog/Scenarios/TreeViewFileSystem.cs
  43. 2 2
      UICatalog/Scenarios/Unicode.cs
  44. 3 11
      UICatalog/Scenarios/WindowsAndFrameViews.cs
  45. 4 5
      UICatalog/Scenarios/WizardAsView.cs
  46. 2 2
      UICatalog/Scenarios/Wizards.cs
  47. 401 390
      UICatalog/UICatalog.cs
  48. 261 910
      UnitTests/ApplicationTests.cs
  49. 3 3
      UnitTests/MainLoopTests.cs
  50. 695 0
      UnitTests/MdiTests.cs
  51. 105 0
      UnitTests/RunStateTests.cs
  52. 9 3
      UnitTests/ScenarioTests.cs
  53. 83 0
      UnitTests/SynchronizatonContextTests.cs
  54. 2 0
      UnitTests/ToplevelTests.cs

+ 204 - 86
Terminal.Gui/Core/Application.cs

@@ -39,6 +39,7 @@ namespace Terminal.Gui {
 	/// };
 	/// Application.Top.Add(win);
 	/// Application.Run();
+	/// Application.Shutdown();
 	/// </code>
 	/// </example>
 	/// <remarks>
@@ -222,19 +223,28 @@ namespace Terminal.Gui {
 		public static bool ExitRunLoopAfterFirstIteration { get; set; } = false;
 
 		/// <summary>
-		/// Notify that a new <see cref="RunState"/> token was created,
-		/// used if <see cref="ExitRunLoopAfterFirstIteration"/> is true.
+		/// Notify that a new <see cref="RunState"/> was created (<see cref="Begin(Toplevel)"/> was called). The token is created in 
+		/// <see cref="Begin(Toplevel)"/> and this event will be fired before that function exits.
 		/// </summary>
+		/// <remarks>
+		///	If <see cref="ExitRunLoopAfterFirstIteration"/> is <see langword="true"/> callers to
+		///	<see cref="Begin(Toplevel)"/> must also subscribe to <see cref="NotifyStopRunState"/>
+		///	and manually dispose of the <see cref="RunState"/> token when the application is done.
+		/// </remarks>
 		public static event Action<RunState> NotifyNewRunState;
 
 		/// <summary>
-		/// Notify that a existent <see cref="RunState"/> token is stopping,
-		/// used if <see cref="ExitRunLoopAfterFirstIteration"/> is true.
+		/// Notify that a existent <see cref="RunState"/> is stopping (<see cref="End(RunState)"/> was called).
 		/// </summary>
+		/// <remarks>
+		///	If <see cref="ExitRunLoopAfterFirstIteration"/> is <see langword="true"/> callers to
+		///	<see cref="Begin(Toplevel)"/> must also subscribe to <see cref="NotifyStopRunState"/>
+		///	and manually dispose of the <see cref="RunState"/> token when the application is done.
+		/// </remarks>
 		public static event Action<Toplevel> NotifyStopRunState;
 
 		/// <summary>
-		///   This event is raised on each iteration of the <see cref="MainLoop"/> 
+		///   This event is raised on each iteration of the <see cref="MainLoop"/>. 
 		/// </summary>
 		/// <remarks>
 		///   See also <see cref="Timeout"/>
@@ -302,31 +312,51 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Initializes a new instance of <see cref="Terminal.Gui"/> Application. 
 		/// </summary>
-		/// <remarks>
 		/// <para>
 		/// Call this method once per instance (or after <see cref="Shutdown"/> has been called).
 		/// </para>
 		/// <para>
-		/// Loads the right <see cref="ConsoleDriver"/> for the platform.
+		/// This function loads the right <see cref="ConsoleDriver"/> for the platform, 
+		/// Creates a <see cref="Toplevel"/>. and assigns it to <see cref="Top"/>
 		/// </para>
 		/// <para>
-		/// Creates a <see cref="Toplevel"/> and assigns it to <see cref="Top"/>
+		/// <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 restored.
 		/// </para>
-		/// </remarks>
-		public static void Init (ConsoleDriver driver = null, IMainLoopDriver mainLoopDriver = null) => Init (() => Toplevel.Create (), driver, mainLoopDriver);
+		/// <para>
+		/// The <see cref="Run{T}(Func{Exception, bool}, ConsoleDriver, IMainLoopDriver)"/> function 
+		/// combines <see cref="Init(ConsoleDriver, IMainLoopDriver)"/> and <see cref="Run(Toplevel, Func{Exception, bool})"/>
+		/// into a single call. An applciation cam use <see cref="Run{T}(Func{Exception, bool}, ConsoleDriver, IMainLoopDriver)"/> 
+		/// without explicitly calling <see cref="Init(ConsoleDriver, IMainLoopDriver)"/>.
+		/// </para>
+		/// <param name="driver">The <see cref="ConsoleDriver"/> to use. If not specified the default driver for the
+		/// platform will be used (see <see cref="WindowsDriver"/>, <see cref="CursesDriver"/>, and <see cref="NetDriver"/>).</param>
+		/// <param name="mainLoopDriver">Specifies the <see cref="MainLoop"/> to use.</param>
+		public static void Init (ConsoleDriver driver = null, IMainLoopDriver mainLoopDriver = null) => Init (() => Toplevel.Create (), driver, mainLoopDriver, resetState: true);
 
 		internal static bool _initialized = false;
 		internal static int _mainThreadId = -1;
 
 		/// <summary>
-		/// Initializes the Terminal.Gui application
+		/// Internal function for initializing a Terminal.Gui application with a <see cref="Toplevel"/> factory object, 
+		/// a <see cref="ConsoleDriver"/>, and <see cref="MainLoop"/>.
+		/// <para>
+		/// This is a low-level function; most applications will use <see cref="Init(ConsoleDriver, IMainLoopDriver)"/> as it is simpler.</para>
 		/// </summary>
-		static void Init (Func<Toplevel> topLevelFactory, ConsoleDriver driver = null, IMainLoopDriver mainLoopDriver = null)
+		/// <param name="topLevelFactory">Specifies the <see cref="Toplevel"> factory funtion.</see>/></param>
+		/// <param name="driver">The <see cref="ConsoleDriver"/> to use. If not specified the default driver for the
+		/// platform will be used (see <see cref="WindowsDriver"/>, <see cref="CursesDriver"/>, and <see cref="NetDriver"/>).</param>
+		/// <param name="mainLoopDriver">Specifies the <see cref="MainLoop"/> to use.</param>
+		/// <param name="resetState">If <see langword="true"/> (default) all <see cref="Application"/> state will be reset. 
+		/// Set to <see langword="false"/> to not reset the state (for when this function is called via 
+		/// <see cref="Run{T}(Func{Exception, bool}, ConsoleDriver, IMainLoopDriver)"/> when <see cref="Init(ConsoleDriver, IMainLoopDriver)"/>
+		/// has not already been called. f</param>
+		internal static void Init (Func<Toplevel> topLevelFactory, ConsoleDriver driver = null, IMainLoopDriver mainLoopDriver = null, bool resetState = true)
 		{
 			if (_initialized && driver == null) return;
 
 			if (_initialized) {
-				throw new InvalidOperationException ("Init must be bracketed by Shutdown");
+				throw new InvalidOperationException ("Init has already been called and must be bracketed by Shutdown.");
 			}
 
 			// Used only for start debugging on Unix.
@@ -338,7 +368,9 @@ namespace Terminal.Gui {
 			//#endif
 
 			// Reset all class variables (Application is a singleton).
-			ResetState ();
+			if (resetState) {
+				ResetState ();
+			}
 
 			// This supports Unit Tests and the passing of a mock driver/loopdriver
 			if (driver != null) {
@@ -346,9 +378,6 @@ namespace Terminal.Gui {
 					throw new ArgumentNullException ("mainLoopDriver cannot be null if driver is provided.");
 				}
 				Driver = driver;
-				Driver.Init (TerminalResized);
-				MainLoop = new MainLoop (mainLoopDriver);
-				SynchronizationContext.SetSynchronizationContext (new MainLoopSyncContext (MainLoop));
 			}
 
 			if (Driver == null) {
@@ -363,10 +392,12 @@ namespace Terminal.Gui {
 					mainLoopDriver = new UnixMainLoop ();
 					Driver = new CursesDriver ();
 				}
-				Driver.Init (TerminalResized);
-				MainLoop = new MainLoop (mainLoopDriver);
-				SynchronizationContext.SetSynchronizationContext (new MainLoopSyncContext (MainLoop));
 			}
+			MainLoop = new MainLoop (mainLoopDriver);
+
+			Driver.Init (TerminalResized);
+			SynchronizationContext.SetSynchronizationContext (new MainLoopSyncContext (MainLoop));
+
 			Top = topLevelFactory ();
 			Current = Top;
 			supportedCultures = GetSupportedCultures ();
@@ -375,7 +406,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Captures the execution state for the provided <see cref="Toplevel"/>  view.
+		/// Captures the execution state for the provided <see cref="Toplevel"/> view.
 		/// </summary>
 		public class RunState : IDisposable {
 			/// <summary>
@@ -391,31 +422,61 @@ namespace Terminal.Gui {
 			/// </summary>
 			public Toplevel Toplevel { get; internal set; }
 
+#if DEBUG_IDISPOSABLE
+			/// <summary>
+			/// For debug purposes to verify objects are being disposed properly
+			/// </summary>
+			public bool WasDisposed = false;
+			/// <summary>
+			/// For debug purposes to verify objects are being disposed properly
+			/// </summary>
+			public int DisposedCount = 0;
+			/// <summary>
+			/// For debug purposes
+			/// </summary>
+			public static List<RunState> Instances = new List<RunState> ();
+			/// <summary>
+			/// For debug purposes
+			/// </summary>
+			public RunState ()
+			{
+				Instances.Add (this);
+			}
+#endif
+
 			/// <summary>
-			/// Releases alTop = l resource used by the <see cref="Application.RunState"/> object.
+			/// Releases all resource used by the <see cref="Application.RunState"/> object.
 			/// </summary>
-			/// <remarks>Call <see cref="Dispose()"/> when you are finished using the <see cref="Application.RunState"/>. The
+			/// <remarks>
+			/// Call <see cref="Dispose()"/> when you are finished using the <see cref="Application.RunState"/>. 
+			/// </remarks>
+			/// <remarks>
 			/// <see cref="Dispose()"/> method leaves the <see cref="Application.RunState"/> in an unusable state. After
 			/// calling <see cref="Dispose()"/>, you must release all references to the
 			/// <see cref="Application.RunState"/> so the garbage collector can reclaim the memory that the
-			/// <see cref="Application.RunState"/> was occupying.</remarks>
+			/// <see cref="Application.RunState"/> was occupying.
+			/// </remarks>
 			public void Dispose ()
 			{
 				Dispose (true);
 				GC.SuppressFinalize (this);
+#if DEBUG_IDISPOSABLE
+				WasDisposed = true;
+#endif
 			}
 
 			/// <summary>
-			/// Dispose the specified disposing.
+			/// Releases all resource used by the <see cref="Application.RunState"/> object.
 			/// </summary>
-			/// <returns>The dispose.</returns>
-			/// <param name="disposing">If set to <c>true</c> disposing.</param>
+			/// <param name="disposing">If set to <see langword="true"/> we are disposing and should dispose held objects.</param>
 			protected virtual void Dispose (bool disposing)
 			{
 				if (Toplevel != null && disposing) {
-					End (Toplevel);
-					Toplevel.Dispose ();
-					Toplevel = null;
+					throw new InvalidOperationException ("You must clean up (Dispose) the Toplevel before calling Application.RunState.Dispose");
+					// BUGBUG: It's insidious that we call EndFirstTopLevel here so I moved it to End.
+					//EndFirstTopLevel (Toplevel);
+					//Toplevel.Dispose ();
+					//Toplevel = null;
 				}
 			}
 		}
@@ -802,8 +863,8 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Building block API: Prepares the provided <see cref="Toplevel"/>  for execution.
 		/// </summary>
-		/// <returns>The runstate handle that needs to be passed to the <see cref="End(RunState)"/> method upon completion.</returns>
-		/// <param name="toplevel">Toplevel to prepare execution for.</param>
+		/// <returns>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>
 		/// <remarks>
 		///  This method prepares the provided toplevel for running with the focus,
 		///  it adds this to the list of toplevels, sets up the mainloop to process the
@@ -816,13 +877,12 @@ namespace Terminal.Gui {
 		{
 			if (toplevel == null) {
 				throw new ArgumentNullException (nameof (toplevel));
-			} else if (toplevel.IsMdiContainer && MdiTop != null) {
+			} else if (toplevel.IsMdiContainer && MdiTop != toplevel && MdiTop != null) {
 				throw new InvalidOperationException ("Only one Mdi Container is allowed.");
 			}
 
 			var rs = new RunState (toplevel);
-
-			Init ();
+			
 			if (toplevel is ISupportInitializeNotification initializableNotification &&
 			    !initializableNotification.IsInitialized) {
 				initializableNotification.BeginInit ();
@@ -833,6 +893,13 @@ namespace Terminal.Gui {
 			}
 
 			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 != null && toplevel != Top && !toplevels.Contains(Top)) {
+					Top.Dispose ();
+					Top = null;
+				}
 				if (string.IsNullOrEmpty (toplevel.Id.ToString ())) {
 					var count = 1;
 					var id = (toplevels.Count + count).ToString ();
@@ -854,7 +921,8 @@ namespace Terminal.Gui {
 					throw new ArgumentException ("There are duplicates toplevels Id's");
 				}
 			}
-			if (toplevel.IsMdiContainer) {
+			// Fix $520 - Set Top = toplevel if Top == null
+			if (Top == null || toplevel.IsMdiContainer) {
 				Top = toplevel;
 			}
 
@@ -893,13 +961,14 @@ namespace Terminal.Gui {
 				Driver.Refresh ();
 			}
 
+			NotifyNewRunState?.Invoke (rs);
 			return rs;
 		}
 
 		/// <summary>
-		/// Building block API: completes the execution of a <see cref="Toplevel"/>  that was started with <see cref="Begin(Toplevel)"/> .
+		/// Building block API: completes the execution of a <see cref="Toplevel"/> that was started with <see cref="Begin(Toplevel)"/> .
 		/// </summary>
-		/// <param name="runState">The runstate returned by the <see cref="Begin(Toplevel)"/> method.</param>
+		/// <param name="runState">The <see cref="RunState"/> returned by the <see cref="Begin(Toplevel)"/> method.</param>
 		public static void End (RunState runState)
 		{
 			if (runState == null)
@@ -910,12 +979,52 @@ namespace Terminal.Gui {
 			} else {
 				runState.Toplevel.OnUnloaded ();
 			}
+
+			// End the RunState.Toplevel 
+			// First, take it off the toplevel Stack
+			if (toplevels.Count > 0) {
+				if (toplevels.Peek () != runState.Toplevel) {
+					// If there the top of the stack is not the RunState.Toplevel then
+					// this call to End is not balanced with the call to Begin that started the RunState
+					throw new ArgumentException ("End must be balanced with calls to Begin");
+				}
+				toplevels.Pop ();
+			}
+
+			// Notify that it is closing
+			runState.Toplevel?.OnClosed (runState.Toplevel);
+
+			// If there is a MdiTop that is not the RunState.Toplevel then runstate.TopLevel 
+			// is a child of MidTop and we should notify the MdiTop that it is closing
+			if (MdiTop != null && !(runState.Toplevel).Modal && runState.Toplevel != MdiTop) {
+				MdiTop.OnChildClosed (runState.Toplevel);
+			}
+
+			// Set Current and Top to the next TopLevel on the stack
+			if (toplevels.Count == 0) {
+				Current = null;
+			} else {
+				Current = toplevels.Peek ();
+				if (toplevels.Count == 1 && Current == MdiTop) {
+					MdiTop.OnAllChildClosed ();
+				} else {
+					SetCurrentAsTop ();
+				}
+				Refresh ();
+			}
+
+			runState.Toplevel?.Dispose ();
+			runState.Toplevel = null;
 			runState.Dispose ();
 		}
 
 		/// <summary>
-		/// Shutdown an application initialized with <see cref="Init(ConsoleDriver, IMainLoopDriver)"/>
+		/// Shutdown an application initialized with <see cref="Init(ConsoleDriver, IMainLoopDriver)"/>.
 		/// </summary>
+		/// <remarks>
+		/// Shutdown must be called for every call to <see cref="Init(ConsoleDriver, IMainLoopDriver)"/> or <see cref="Application.Run(Toplevel, Func{Exception, bool})"/>
+		/// to ensure all resources are cleaned up (Disposed) and terminal settings are restored.
+		/// </remarks>
 		public static void Shutdown ()
 		{
 			ResetState ();
@@ -930,15 +1039,17 @@ namespace Terminal.Gui {
 			// Shutdown is the bookend for Init. As such it needs to clean up all resources
 			// Init created. Apps that do any threading will need to code defensively for this.
 			// e.g. see Issue #537
-			// TODO: Some of this state is actually related to Begin/End (not Init/Shutdown) and should be moved to `RunState` (#520)
 			foreach (var t in toplevels) {
 				t.Running = false;
 				t.Dispose ();
 			}
 			toplevels.Clear ();
 			Current = null;
+			Top?.Dispose ();
 			Top = null;
 
+			// BUGBUG: MdiTop is not cleared here, but it should be?
+
 			MainLoop = null;
 			Driver?.End ();
 			Driver = null;
@@ -990,40 +1101,17 @@ namespace Terminal.Gui {
 			Driver.Refresh ();
 		}
 
-		internal static void End (View view)
-		{
-			if (toplevels.Peek () != view)
-				throw new ArgumentException ("The view that you end with must be balanced");
-			toplevels.Pop ();
-
-			(view as Toplevel)?.OnClosed ((Toplevel)view);
 
-			if (MdiTop != null && !((Toplevel)view).Modal && view != MdiTop) {
-				MdiTop.OnChildClosed (view as Toplevel);
-			}
-
-			if (toplevels.Count == 0) {
-				Current = null;
-			} else {
-				Current = toplevels.Peek ();
-				if (toplevels.Count == 1 && Current == MdiTop) {
-					MdiTop.OnAllChildClosed ();
-				} else {
-					SetCurrentAsTop ();
-				}
-				Refresh ();
-			}
-		}
 
 		/// <summary>
-		///   Building block API: Runs the main loop for the created dialog
+		///   Building block API: Runs the <see cref="MainLoop"/> for the created <see cref="Toplevel"/>.
 		/// </summary>
 		/// <remarks>
-		///   Use the wait parameter to control whether this is a
-		///   blocking or non-blocking call.
+		///   Use the <paramref name="wait"/> parameter to control whether this is a blocking or non-blocking call.
 		/// </remarks>
-		/// <param name="state">The state returned by the Begin method.</param>
-		/// <param name="wait">By default this is true which will execute the runloop waiting for events, if you pass false, you can use this method to run a single iteration of the events.</param>
+		/// <param name="state">The state returned by the <see cref="Begin(Toplevel)"/> method.</param>
+		/// <param name="wait">By default this is <see langword="true"/> which will execute the runloop waiting for events, 
+		/// if set to <see langword="false"/>, a single iteration will execute.</param>
 		public static void RunLoop (RunState state, bool wait = true)
 		{
 			if (state == null)
@@ -1033,18 +1121,21 @@ namespace Terminal.Gui {
 
 			bool firstIteration = true;
 			for (state.Toplevel.Running = true; state.Toplevel.Running;) {
-				if (ExitRunLoopAfterFirstIteration && !firstIteration)
+				if (ExitRunLoopAfterFirstIteration && !firstIteration) {
 					return;
+				}
 				RunMainLoopIteration (ref state, wait, ref firstIteration);
 			}
 		}
 
 		/// <summary>
-		/// Run one iteration of the MainLoop.
+		/// Run one iteration of the <see cref="MainLoop"/>.
 		/// </summary>
-		/// <param name="state">The state returned by the Begin method.</param>
-		/// <param name="wait">If will execute the runloop waiting for events.</param>
-		/// <param name="firstIteration">If it's the first run loop iteration.</param>
+		/// <param name="state">The state returned by <see cref="Begin(Toplevel)"/>.</param>
+		/// <param name="wait">If <see langword="true"/> will execute the runloop waiting for events. If <see langword="true"/>
+		/// will return after a single iteration.</param>
+		/// <param name="firstIteration">Set to <see langword="true"/> if this is the first run loop iteration. Upon return,
+		/// it will be set to <see langword="false"/> if at least one iteration happened.</param>
 		public static void RunMainLoopIteration (ref RunState state, bool wait, ref bool firstIteration)
 		{
 			if (MainLoop.EventsPending (wait)) {
@@ -1145,17 +1236,36 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Runs the application by calling <see cref="Run(Toplevel, Func{Exception, bool})"/> with the value of <see cref="Top"/>
+		/// Runs the application by calling <see cref="Run(Toplevel, Func{Exception, bool})"/> with the value of <see cref="Top"/>.
 		/// </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);
 		}
 
 		/// <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 calling <see cref="Run(Toplevel, Func{Exception, bool})"/> 
+		/// with a new instance of the specified <see cref="Toplevel"/>-derived class.
+		/// <para>
+		/// If <see cref="Init(ConsoleDriver, IMainLoopDriver)"/> has not arleady been called, this function will
+		/// call <see cref="Init(Func{Toplevel}, ConsoleDriver, IMainLoopDriver, bool)"/>.
+		/// </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>
-		public static void Run<T> (Func<Exception, bool> errorHandler = null) where T : Toplevel, new()
+		/// <remarks>
+		/// See <see cref="Run(Toplevel, Func{Exception, bool})"/> for more details.
+		/// </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 <see cref="WindowsDriver"/>, <see cref="CursesDriver"/>, and <see cref="NetDriver"/>).</param>
+		/// <param name="mainLoopDriver">Specifies the <see cref="MainLoop"/> to use.</param>
+		public static void Run<T> (Func<Exception, bool> errorHandler = null, ConsoleDriver driver = null, IMainLoopDriver mainLoopDriver = null) where T : Toplevel, new()
 		{
 			if (_initialized && Driver != null) {
 				var top = new T ();
@@ -1166,9 +1276,14 @@ namespace Terminal.Gui {
 				if (type != typeof (Toplevel)) {
 					throw new ArgumentException ($"{top.GetType ().Name} must be derived from TopLevel");
 				}
+				// Run() will eventually cause Application.Top to be set, via Begin() and SetCurrentAsTop()
 				Run (top, errorHandler);
 			} else {
-				Init (() => new T ());
+				if (!_initialized && driver == null) {
+					throw new ArgumentException ("Init has not been called; a valid driver and mainloop must be provided");
+				}
+				// Note in this case, we don't verify the type of the Toplevel created by new T(). 
+				Init (() => new T (), Driver == null ? driver : Driver, Driver == null ? mainLoopDriver : null, resetState: false);
 				Run (Top, errorHandler);
 			}
 		}
@@ -1192,16 +1307,19 @@ namespace Terminal.Gui {
 		///   <para>
 		///     Alternatively, to have a program control the main loop and 
 		///     process events manually, call <see cref="Begin(Toplevel)"/> to set things up manually and then
-		///     repeatedly call <see cref="RunLoop(RunState, bool)"/> with the wait parameter set to false.   By doing this
+		///     repeatedly call <see cref="RunLoop(RunState, bool)"/> with the wait parameter set to false. By doing this
 		///     the <see cref="RunLoop(RunState, bool)"/> method will only process any pending events, timers, idle handlers and
 		///     then return control immediately.
 		///   </para>
 		///   <para>
-		///     When <paramref name="errorHandler"/> is null the exception is rethrown, when it returns true the application is resumed and when false method exits gracefully.
+		///     RELEASE builds only: When <paramref name="errorHandler"/> is <see langword="null"/> any exeptions will be rethrown.  
+		///     Otheriwse, if <paramref name="errorHandler"/> will be called. If <paramref name="errorHandler"/> 
+		///     returns <see langword="true"/> the <see cref="RunLoop(RunState, bool)"/> will resume; otherwise 
+		///     this method will exit.
 		///   </para>
 		/// </remarks>
 		/// <param name="view">The <see cref="Toplevel"/> to run modally.</param>
-		/// <param name="errorHandler">Handler for any unhandled exceptions (resumes when returns true, rethrows when null).</param>
+		/// <param name="errorHandler">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)
 		{
 			var resume = true;
@@ -1211,13 +1329,12 @@ namespace Terminal.Gui {
 #endif
 				resume = false;
 				var runToken = Begin (view);
+				// If ExitRunLoopAfterFirstIteration is true then the user must dispose of the runToken
+				// by using NotifyStopRunState event.
 				RunLoop (runToken);
-				if (!ExitRunLoopAfterFirstIteration)
+				if (!ExitRunLoopAfterFirstIteration) {
 					End (runToken);
-				else
-					// If ExitRunLoopAfterFirstIteration is true then the user must deal his disposing when it ends
-					// by using NotifyStopRunState event.
-					NotifyNewRunState?.Invoke (runToken);
+				}
 #if !DEBUG
 				}
 				catch (Exception error)
@@ -1310,8 +1427,9 @@ namespace Terminal.Gui {
 
 		static void OnNotifyStopRunState (Toplevel top)
 		{
-			if (ExitRunLoopAfterFirstIteration)
+			if (ExitRunLoopAfterFirstIteration) {
 				NotifyStopRunState?.Invoke (top);
+			}
 		}
 
 		/// <summary>

+ 2 - 2
Terminal.Gui/Core/MainLoop.cs

@@ -94,8 +94,8 @@ namespace Terminal.Gui {
 		public IMainLoopDriver Driver { get; }
 
 		/// <summary>
-		/// Invoked when a new timeout is added to be used on the case
-		/// if <see cref="Application.ExitRunLoopAfterFirstIteration"/> is true,
+		/// Invoked when a new timeout is added. To be used in the case
+		/// when <see cref="Application.ExitRunLoopAfterFirstIteration"/> is <see langword="true"/>.
 		/// </summary>
 		public event Action<long> TimeoutAdded;
 

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

@@ -44,7 +44,7 @@ namespace Terminal.Gui {
 		public bool Running { get; set; }
 
 		/// <summary>
-		/// Invoked when the Toplevel <see cref="Application.RunState"/> has begin loaded.
+		/// Invoked when the Toplevel <see cref="Application.RunState"/> has begun to be loaded.
 		/// A Loaded event handler is a good place to finalize initialization before calling 
 		/// <see cref="Application.RunLoop(Application.RunState, bool)"/>.
 		/// </summary>
@@ -77,13 +77,13 @@ namespace Terminal.Gui {
 
 		/// <summary>
 		/// Invoked when a child of the Toplevel <see cref="Application.RunState"/> is closed by  
-		/// <see cref="Application.End(View)"/>.
+		/// <see cref="Application.End(Application.RunState)"/>.
 		/// </summary>
 		public event Action<Toplevel> ChildClosed;
 
 		/// <summary>
 		/// Invoked when the last child of the Toplevel <see cref="Application.RunState"/> is closed from 
-		/// by <see cref="Application.End(View)"/>.
+		/// by <see cref="Application.End(Application.RunState)"/>.
 		/// </summary>
 		public event Action AllChildClosed;
 
@@ -94,7 +94,7 @@ namespace Terminal.Gui {
 		public event Action<ToplevelClosingEventArgs> Closing;
 
 		/// <summary>
-		/// Invoked when the Toplevel's <see cref="Application.RunState"/> is closed by <see cref="Application.End(View)"/>.
+		/// Invoked when the Toplevel's <see cref="Application.RunState"/> is closed by <see cref="Application.End(Application.RunState)"/>.
 		/// </summary>
 		public event Action<Toplevel> Closed;
 

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

@@ -1496,7 +1496,7 @@ namespace Terminal.Gui {
 			if (Border != null) {
 				Border.DrawContent (this);
 			} else if (ustring.IsNullOrEmpty (TextFormatter.Text) &&
-				(GetType ().IsPublic || GetType ().IsNestedPublic) && !IsOverridden (this, "Redraw") &&
+				(GetType ().IsNestedPublic) && !IsOverridden (this, "Redraw") &&
 				(!NeedDisplay.IsEmpty || ChildNeedsDisplay || LayoutNeeded)) {
 
 				Clear ();

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

@@ -159,7 +159,7 @@ namespace Terminal.Gui {
 			public event Action<TitleEventArgs> TitleChanged;
 
 			// The contentView works like the ContentView in FrameView.
-			private View contentView = new View ();
+			private View contentView = new View () { Data = "WizardContentView" };
 
 			/// <summary>
 			/// Sets or gets help text for the <see cref="WizardStep"/>.If <see cref="WizardStep.HelpText"/> is empty

+ 4 - 0
UICatalog/Properties/launchSettings.json

@@ -48,6 +48,10 @@
     "WSL": {
       "commandName": "WSL2",
       "distributionName": ""
+    },
+    "All Views Tester": {
+      "commandName": "Project",
+      "commandLineArgs": "\"All Views Tester\""
     }
   }
 }

+ 8 - 14
UICatalog/Scenario.cs

@@ -48,12 +48,7 @@ namespace UICatalog {
 		private bool _disposedValue;
 
 		/// <summary>
-		/// The Top level for the <see cref="Scenario"/>. This should be set to <see cref="Terminal.Gui.Application.Top"/> in most cases.
-		/// </summary>
-		public Toplevel Top { get; set; }
-
-		/// <summary>
-		/// The Window for the <see cref="Scenario"/>. This should be set within the <see cref="Terminal.Gui.Application.Top"/> in most cases.
+		/// 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; }
 
@@ -63,22 +58,21 @@ namespace UICatalog {
 		/// the Scenario picker UI.
 		/// Override <see cref="Init"/> to provide any <see cref="Terminal.Gui.Toplevel"/> behavior needed.
 		/// </summary>
-		/// <param name="top">The Toplevel created by the UI Catalog host.</param>
 		/// <param name="colorScheme">The colorscheme to use.</param>
 		/// <remarks>
 		/// <para>
-		/// The base implementation calls <see cref="Application.Init"/>, sets <see cref="Top"/> to the passed in <see cref="Toplevel"/>, creates a <see cref="Window"/> for <see cref="Win"/> and adds it to <see cref="Top"/>.
+		/// The base implementation calls <see cref="Application.Init"/> and creates a <see cref="Window"/> for <see cref="Win"/> 
+		/// and adds it to <see cref="Application.Top"/>.
 		/// </para>
 		/// <para>
-		/// Overrides that do not call the base.<see cref="Run"/>, must call <see cref="Application.Init"/> before creating any views or calling other Terminal.Gui APIs.
+		/// Overrides that do not call the base.<see cref="Run"/>, must call <see cref="Application.Init"/> 
+		/// before creating any views or calling other Terminal.Gui APIs.
 		/// </para>
 		/// </remarks>
-		public virtual void Init (Toplevel top, ColorScheme colorScheme)
+		public virtual void Init (ColorScheme colorScheme)
 		{
 			Application.Init ();
 
-			Top = top != null ? top : Application.Top;
-
 			Win = new Window ($"CTRL-Q to Close - Scenario: {GetName ()}") {
 				X = 0,
 				Y = 0,
@@ -86,7 +80,7 @@ namespace UICatalog {
 				Height = Dim.Fill (),
 				ColorScheme = colorScheme,
 			};
-			Top.Add (Win);
+			Application.Top.Add (Win);
 		}
 
 		/// <summary>
@@ -201,7 +195,7 @@ namespace UICatalog {
 		public virtual void Run ()
 		{
 			// Must explicit call Application.Shutdown method to shutdown.
-			Application.Run (Top);
+			Application.Run (Application.Top);
 		}
 
 		/// <summary>

+ 10 - 24
UICatalog/Scenarios/AllViewsTester.cs

@@ -14,7 +14,7 @@ namespace UICatalog.Scenarios {
 	[ScenarioCategory ("Tests")]
 	[ScenarioCategory ("Top Level Windows")]
 	public class AllViewsTester : Scenario {
-		Window _leftPane;
+		FrameView _leftPane;
 		ListView _classListView;
 		FrameView _hostPane;
 
@@ -40,42 +40,33 @@ namespace UICatalog.Scenarios {
 		TextField _hText;
 		int _hVal = 0;
 
-		public override void Init (Toplevel top, ColorScheme colorScheme)
+		public override void Init (ColorScheme colorScheme)
 		{
 			Application.Init ();
-
-			Top = top != null ? top : Application.Top;
-
-			//Win = new Window ($"CTRL-Q to Close - Scenario: {GetName ()}") {
-			//	X = 0,
-			//	Y = 0,
-			//	Width = Dim.Fill (),
-			//	Height = Dim.Fill ()
-			//};
-			//Top.Add (Win);
+			// Don't create a sub-win; just use Applicatiion.Top
 		}
-
+		
 		public override void Setup ()
 		{
 			var statusBar = new StatusBar (new StatusItem [] {
 				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
 				new StatusItem(Key.F2, "~F2~ Toggle Frame Ruler", () => {
 					ConsoleDriver.Diagnostics ^= ConsoleDriver.DiagnosticFlags.FrameRuler;
-					Top.SetNeedsDisplay ();
+					Application.Top.SetNeedsDisplay ();
 				}),
 				new StatusItem(Key.F3, "~F3~ Toggle Frame Padding", () => {
 					ConsoleDriver.Diagnostics ^= ConsoleDriver.DiagnosticFlags.FramePadding;
-					Top.SetNeedsDisplay ();
+					Application.Top.SetNeedsDisplay ();
 				}),
 			});
-			Top.Add (statusBar);
+			Application.Top.Add (statusBar);
 
 			_viewClasses = GetAllViewClassesCollection ()
 				.OrderBy (t => t.Name)
 				.Select (t => new KeyValuePair<string, Type> (t.Name, t))
 				.ToDictionary (t => t.Key, t => t.Value);
 
-			_leftPane = new Window ("Classes") {
+			_leftPane = new FrameView ("Classes") {
 				X = 0,
 				Y = 0,
 				Width = 15,
@@ -241,9 +232,9 @@ namespace UICatalog.Scenarios {
 				ColorScheme = Colors.Dialog,
 			};
 
-			Top.Add (_leftPane, _settingsPane, _hostPane);
+			Application.Top.Add (_leftPane, _settingsPane, _hostPane);
 
-			Top.LayoutSubviews ();
+			Application.Top.LayoutSubviews ();
 
 			_curView = CreateClass (_viewClasses.First ().Value);
 		}
@@ -438,11 +429,6 @@ namespace UICatalog.Scenarios {
 			UpdateTitle (_curView);
 		}
 
-		public override void Run ()
-		{
-			base.Run ();
-		}
-
 		private void Quit ()
 		{
 			Application.RequestStop ();

+ 3 - 6
UICatalog/Scenarios/BackgroundWorkerCollection.cs

@@ -12,17 +12,14 @@ namespace UICatalog.Scenarios {
 	[ScenarioCategory ("Dialogs")]
 	[ScenarioCategory ("Controls")]
 	public class BackgroundWorkerCollection : Scenario {
-		public override void Init (Toplevel top, ColorScheme colorScheme)
+		public override void Init (ColorScheme colorScheme)
 		{
-			Application.Top.Dispose ();
-
-			Application.Run<MdiMain> ();
-
-			Application.Top.Dispose ();
+			// Do not call Init as Application.Run<T> will do it
 		}
 
 		public override void Run ()
 		{
+			Application.Run<MdiMain> ();
 		}
 
 		class MdiMain : Toplevel {

+ 4 - 6
UICatalog/Scenarios/BordersComparisons.cs

@@ -5,12 +5,10 @@ namespace UICatalog.Scenarios {
 	[ScenarioCategory ("Layout")]
 	[ScenarioCategory ("Borders")]
 	public class BordersComparisons : Scenario {
-		public override void Init (Toplevel top, ColorScheme colorScheme)
+		public override void Init (ColorScheme colorScheme)
 		{
 			Application.Init ();
 
-			top = Application.Top;
-
 			var borderStyle = BorderStyle.Double;
 			var drawMarginFrame = false;
 			var borderThickness = new Thickness (1, 2, 3, 4);
@@ -53,7 +51,7 @@ namespace UICatalog.Scenarios {
 				Width = 10
 			};
 			win.Add (tf1, button, label, tv, tf2);
-			top.Add (win);
+			Application.Top.Add (win);
 
 			var top2 = new Border.ToplevelContainer (new Rect (50, 5, 40, 20),
 				new Border () {
@@ -92,7 +90,7 @@ namespace UICatalog.Scenarios {
 				Width = 10
 			};
 			top2.Add (tf3, button2, label2, tv2, tf4);
-			top.Add (top2);
+			Application.Top.Add (top2);
 
 			var frm = new FrameView (new Rect (95, 5, 40, 20), "Test3", null,
 				new Border () {
@@ -128,7 +126,7 @@ namespace UICatalog.Scenarios {
 				Width = 10
 			};
 			frm.Add (tf5, button3, label3, tv3, tf6);
-			top.Add (frm);
+			Application.Top.Add (frm);
 
 			Application.Run ();
 		}

+ 2 - 2
UICatalog/Scenarios/Buttons.cs

@@ -56,7 +56,7 @@ namespace UICatalog.Scenarios {
 
 			//View prev = colorButtonsLabel;
 
-			//With this method there is no need to call Top.Ready += () => Top.Redraw (Top.Bounds);
+			//With this method there is no need to call Application.TopReady += () => Application.TopRedraw (Top.Bounds);
 			var x = Pos.Right (colorButtonsLabel) + 2;
 			foreach (var colorScheme in Colors.ColorSchemes) {
 				var colorButton = new Button ($"{colorScheme.Key}") {
@@ -272,7 +272,7 @@ namespace UICatalog.Scenarios {
 				}
 			};
 
-			Top.Ready += () => radioGroup.Refresh ();
+			Application.Top.Ready += () => radioGroup.Refresh ();
 		}
 	}
 }

+ 2 - 2
UICatalog/Scenarios/ClassExplorer.cs

@@ -58,7 +58,7 @@ namespace UICatalog.Scenarios {
 			Win.Title = this.GetName ();
 			Win.Y = 1; // menu
 			Win.Height = Dim.Fill (1); // status bar
-			Top.LayoutSubviews ();
+			Application.Top.LayoutSubviews ();
 
 			var menu = new MenuBar (new MenuBarItem [] {
 				new MenuBarItem ("_File", new MenuItem [] {
@@ -73,7 +73,7 @@ namespace UICatalog.Scenarios {
 				new MenuItem ("_Expand All", "", () => treeView.ExpandAll()),
 				new MenuItem ("_Collapse All", "", () => treeView.CollapseAll()) }),
 			});
-			Top.Add (menu);
+			Application.Top.Add (menu);
 
 			treeView = new TreeView<object> () {
 				X = 0,

+ 4 - 7
UICatalog/Scenarios/Clipping.cs

@@ -7,13 +7,10 @@ namespace UICatalog.Scenarios {
 
 	public class Clipping : Scenario {
 
-		public override void Init (Toplevel top, ColorScheme colorScheme)
+		public override void Init (ColorScheme colorScheme)
 		{
 			Application.Init ();
-
-			Top = top != null ? top : Application.Top;
-
-			Top.ColorScheme = Colors.Base;
+			Application.Top.ColorScheme = Colors.Base;
 		}
 
 		public override void Setup ()
@@ -26,7 +23,7 @@ namespace UICatalog.Scenarios {
 				X = 0, Y = 0,
 				//ColorScheme = Colors.Dialog
 			};
-			Top.Add (label);
+			Application.Top.Add (label);
 
 			var scrollView = new ScrollView (new Rect (3, 3, 50, 20));
 			scrollView.ColorScheme = Colors.Menu;
@@ -69,7 +66,7 @@ namespace UICatalog.Scenarios {
 
 			scrollView.Add (embedded1);
 
-			Top.Add (scrollView);
+			Application.Top.Add (scrollView);
 		}
 	}
 }

+ 8 - 9
UICatalog/Scenarios/CollectionNavigatorTester.cs

@@ -15,11 +15,10 @@ namespace UICatalog.Scenarios {
 	public class CollectionNavigatorTester : Scenario {
 
 		// Don't create a Window, just return the top-level view
-		public override void Init (Toplevel top, ColorScheme colorScheme)
+		public override void Init (ColorScheme colorScheme)
 		{
 			Application.Init ();
-			Top = top != null ? top : Application.Top;
-			Top.ColorScheme = Colors.Base;
+			Application.Top.ColorScheme = Colors.Base;
 		}
 
 		System.Collections.Generic.List<string> _items = new string [] {
@@ -103,7 +102,7 @@ namespace UICatalog.Scenarios {
 				new MenuBarItem("_Quit", "CTRL-Q", () => Quit()),
 			});
 
-			Top.Add (menu);
+			Application.Top.Add (menu);
 
 			_items.Sort (StringComparer.OrdinalIgnoreCase);
 
@@ -113,7 +112,7 @@ namespace UICatalog.Scenarios {
 				Y = 1,
 				Height = Dim.Fill ()
 			};
-			Top.Add (vsep);
+			Application.Top.Add (vsep);
 			CreateTreeView ();
 		}
 
@@ -129,7 +128,7 @@ namespace UICatalog.Scenarios {
 				Width = Dim.Percent (50),
 				Height = 1,
 			};
-			Top.Add (label);
+			Application.Top.Add (label);
 
 			_listView = new ListView () {
 				X = 0,
@@ -140,7 +139,7 @@ namespace UICatalog.Scenarios {
 				AllowsMultipleSelection = false,
 				ColorScheme = Colors.TopLevel
 			};
-			Top.Add (_listView);
+			Application.Top.Add (_listView);
 
 			_listView.SetSource (_items);
 
@@ -161,7 +160,7 @@ namespace UICatalog.Scenarios {
 				Width = Dim.Percent (50),
 				Height = 1,
 			};
-			Top.Add (label);
+			Application.Top.Add (label);
 
 			_treeView = new TreeView () {
 				X = Pos.Right (_listView) + 1,
@@ -170,7 +169,7 @@ namespace UICatalog.Scenarios {
 				Height = Dim.Fill (),
 				ColorScheme = Colors.TopLevel
 			};
-			Top.Add (_treeView);
+			Application.Top.Add (_treeView);
 
 			var root = new TreeNode ("IsLetterOrDigit examples");
 			root.Children = _items.Where (i => char.IsLetterOrDigit (i [0])).Select (i => new TreeNode (i)).Cast<ITreeNode> ().ToList ();

+ 2 - 2
UICatalog/Scenarios/ComputedLayout.cs

@@ -25,12 +25,12 @@ namespace UICatalog.Scenarios {
 					new MenuItem ("_Quit", "", () => Quit()),
 				}),
 			});
-			Top.Add (menu);
+			Application.Top.Add (menu);
 
 			var statusBar = new StatusBar (new StatusItem [] {
 				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
 			});
-			Top.Add (statusBar);
+			Application.Top.Add (statusBar);
 
 			//Top.LayoutStyle = LayoutStyle.Computed;
 			// Demonstrate using Dim to create a horizontal ruler that always measures the parent window's width

+ 1 - 1
UICatalog/Scenarios/ContextMenus.cs

@@ -81,7 +81,7 @@ namespace UICatalog.Scenarios {
 
 			Win.WantMousePositionReports = true;
 
-			Top.Closed += (_) => {
+			Application.Top.Closed += (_) => {
 				Thread.CurrentThread.CurrentUICulture = new CultureInfo ("en-US");
 				Application.RootMouseEvent -= Application_RootMouseEvent;
 			};

+ 3 - 3
UICatalog/Scenarios/CsvEditor.cs

@@ -34,7 +34,7 @@ namespace UICatalog.Scenarios {
 			Win.Title = this.GetName();
 			Win.Y = 1; // menu
 			Win.Height = Dim.Fill (1); // status bar
-			Top.LayoutSubviews ();
+			Application.Top.LayoutSubviews ();
 
 			this.tableView = new TableView () {
 				X = 0,
@@ -70,14 +70,14 @@ namespace UICatalog.Scenarios {
 					miCentered = new MenuItem ("_Set Format Pattern", "", () => SetFormat()),
 				})
 			});
-			Top.Add (menu);
+			Application.Top.Add (menu);
 
 			var statusBar = new StatusBar (new StatusItem [] {
 				new StatusItem(Key.CtrlMask | Key.O, "~^O~ Open", () => Open()),
 				new StatusItem(Key.CtrlMask | Key.S, "~^S~ Save", () => Save()),
 				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
 			});
-			Top.Add (statusBar);
+			Application.Top.Add (statusBar);
 
 			Win.Add (tableView);
 

+ 2 - 2
UICatalog/Scenarios/Dialogs.cs

@@ -116,9 +116,9 @@ namespace UICatalog.Scenarios {
 			{
 				frame.Height = Dim.Height (widthEdit) + Dim.Height (heightEdit) + Dim.Height (titleEdit)
 					+ Dim.Height (numButtonsEdit) + Dim.Height (styleRadioGroup) + Dim.Height(glyphsNotWords) + 2;
-				Top.Loaded -= Top_Loaded;
+				Application.Top.Loaded -= Top_Loaded;
 			}
-			Top.Loaded += Top_Loaded;
+			Application.Top.Loaded += Top_Loaded;
 
 			label = new Label ("Button Pressed:") {
 				X = Pos.Center (),

+ 2 - 3
UICatalog/Scenarios/DynamicMenuBar.cs

@@ -13,11 +13,10 @@ namespace UICatalog.Scenarios {
 	[ScenarioCategory ("Top Level Windows")]
 	[ScenarioCategory ("Menus")]
 	public class DynamicMenuBar : Scenario {
-		public override void Init (Toplevel top, ColorScheme colorScheme)
+		public override void Init (ColorScheme colorScheme)
 		{
 			Application.Init ();
-			Top = Application.Top;
-			Top.Add (new DynamicMenuBarSample ($"CTRL-Q to Close - Scenario: {GetName ()}"));
+			Application.Top.Add (new DynamicMenuBarSample ($"CTRL-Q to Close - Scenario: {GetName ()}"));
 		}
 
 		public class DynamicMenuItemList {

+ 2 - 3
UICatalog/Scenarios/DynamicStatusBar.cs

@@ -12,11 +12,10 @@ namespace UICatalog.Scenarios {
 	[ScenarioMetadata (Name: "Dynamic StatusBar", Description: "Demonstrates how to add and remove a StatusBar and change items dynamically.")]
 	[ScenarioCategory ("Top Level Windows")]
 	public class DynamicStatusBar : Scenario {
-		public override void Init (Toplevel top, ColorScheme colorScheme)
+		public override void Init (ColorScheme colorScheme)
 		{
 			Application.Init ();
-			Top = Application.Top;
-			Top.Add (new DynamicStatusBarSample ($"CTRL-Q to Close - Scenario: {GetName ()}"));
+			Application.Top.Add (new DynamicStatusBarSample ($"CTRL-Q to Close - Scenario: {GetName ()}"));
 		}
 
 		public class DynamicStatusItemList {

+ 7 - 7
UICatalog/Scenarios/Editor.cs

@@ -30,12 +30,12 @@ namespace UICatalog.Scenarios {
 		private TabView _tabView;
 		private MenuItem _miForceMinimumPosToZero;
 		private bool _forceMinimumPosToZero = true;
-		private readonly List<CultureInfo> _cultureInfos = Application.SupportedCultures;
+		private List<CultureInfo> _cultureInfos;
 
-		public override void Init (Toplevel top, ColorScheme colorScheme)
+		public override void Init (ColorScheme colorScheme)
 		{
 			Application.Init ();
-			Top = top != null ? top : Application.Top;
+			_cultureInfos = Application.SupportedCultures;
 
 			Win = new Window (_fileName ?? "Untitled") {
 				X = 0,
@@ -44,7 +44,7 @@ namespace UICatalog.Scenarios {
 				Height = Dim.Fill (),
 				ColorScheme = colorScheme,
 			};
-			Top.Add (Win);
+			Application.Top.Add (Win);
 
 			_textView = new TextView () {
 				X = 0,
@@ -114,7 +114,7 @@ namespace UICatalog.Scenarios {
 				})
 			});
 
-			Top.Add (menu);
+			Application.Top.Add (menu);
 
 			var statusBar = new StatusBar (new StatusItem [] {
 				siCursorPosition,
@@ -124,7 +124,7 @@ namespace UICatalog.Scenarios {
 				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
 				new StatusItem(Key.Null, $"OS Clipboard IsSupported : {Clipboard.IsSupported}", null)
 			});
-			Top.Add (statusBar);
+			Application.Top.Add (statusBar);
 
 			_scrollBar = new ScrollBarView (_textView, true);
 
@@ -196,7 +196,7 @@ namespace UICatalog.Scenarios {
 				}
 			};
 
-			Top.Closed += (_) => Thread.CurrentThread.CurrentUICulture = new CultureInfo ("en-US");
+			Application.Top.Closed += (_) => Thread.CurrentThread.CurrentUICulture = new CultureInfo ("en-US");
 		}
 
 		private void DisposeWinDialog ()

+ 3 - 3
UICatalog/Scenarios/GraphViewExample.cs

@@ -23,7 +23,7 @@ namespace UICatalog.Scenarios {
 			Win.Title = this.GetName ();
 			Win.Y = 1; // menu
 			Win.Height = Dim.Fill (1); // status bar
-			Top.LayoutSubviews ();
+			Application.Top.LayoutSubviews ();
 
 			graphs = new Action [] {
 				 ()=>SetupPeriodicTableScatterPlot(),    //0
@@ -59,7 +59,7 @@ namespace UICatalog.Scenarios {
 				}),
 
 				});
-			Top.Add (menu);
+			Application.Top.Add (menu);
 
 			graphView = new GraphView () {
 				X = 1,
@@ -92,7 +92,7 @@ namespace UICatalog.Scenarios {
 				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
 				new StatusItem(Key.CtrlMask | Key.G, "~^G~ Next", ()=>graphs[currentGraph++%graphs.Length]()),
 			});
-			Top.Add (statusBar);
+			Application.Top.Add (statusBar);
 		}
 
 		private void MultiBarGraph ()

+ 2 - 2
UICatalog/Scenarios/HexEditor.cs

@@ -52,7 +52,7 @@ namespace UICatalog.Scenarios {
 					miAllowEdits = new MenuItem ("_AllowEdits", "", () => ToggleAllowEdits ()){Checked = _hexView.AllowEdits, CheckType = MenuItemCheckStyle.Checked}
 				})
 			});
-			Top.Add (menu);
+			Application.Top.Add (menu);
 
 			statusBar = new StatusBar (new StatusItem [] {
 				new StatusItem(Key.F2, "~F2~ Open", () => Open()),
@@ -61,7 +61,7 @@ namespace UICatalog.Scenarios {
 				siPositionChanged = new StatusItem(Key.Null,
 					$"Position: {_hexView.Position} Line: {_hexView.CursorPosition.Y} Col: {_hexView.CursorPosition.X} Line length: {_hexView.BytesPerLine}", () => {})
 			});
-			Top.Add (statusBar);
+			Application.Top.Add (statusBar);
 		}
 
 		private void _hexView_PositionChanged (HexView.HexViewEventArgs obj)

+ 3 - 3
UICatalog/Scenarios/InteractiveTree.cs

@@ -20,14 +20,14 @@ namespace UICatalog.Scenarios {
 			Win.Title = this.GetName ();
 			Win.Y = 1; // menu
 			Win.Height = Dim.Fill (1); // status bar
-			Top.LayoutSubviews ();
+			Application.Top.LayoutSubviews ();
 
 			var menu = new MenuBar (new MenuBarItem [] {
 				new MenuBarItem ("_File", new MenuItem [] {
 					new MenuItem ("_Quit", "", () => Quit()),
 				})
 				});
-			Top.Add (menu);
+			Application.Top.Add (menu);
 
 			treeView = new TreeView () {
 				X = 0,
@@ -45,7 +45,7 @@ namespace UICatalog.Scenarios {
 				new StatusItem(Key.CtrlMask | Key.T, "~^T~ Add Root", () => AddRootNode()),
 				new StatusItem(Key.CtrlMask | Key.R, "~^R~ Rename Node", () => RenameNode()),
 			});
-			Top.Add (statusBar);
+			Application.Top.Add (statusBar);
 
 		}
 

+ 6 - 7
UICatalog/Scenarios/Keys.cs

@@ -48,10 +48,9 @@ namespace UICatalog.Scenarios {
 			}
 		}
 
-		public override void Init (Toplevel top, ColorScheme colorScheme)
+		public override void Init (ColorScheme colorScheme)
 		{
 			Application.Init ();
-			Top = top != null ? top : Application.Top;
 			
 			Win = new TestWindow ($"CTRL-Q to Close - Scenario: {GetName ()}") {
 				X = 0,
@@ -60,7 +59,7 @@ namespace UICatalog.Scenarios {
 				Height = Dim.Fill (),
 				ColorScheme = colorScheme,
 			};
-			Top.Add (Win);
+			Application.Top.Add (Win);
 		}
 
 		public override void Setup ()
@@ -107,7 +106,7 @@ namespace UICatalog.Scenarios {
 				Shift = true
 			});
 			var maxLogEntry = $"Key{"",-5}: {fakeKeyPress}".Length;
-			var yOffset = (Top == Application.Top ? 1 : 6);
+			var yOffset = (Application.Top == Application.Top ? 1 : 6);
 			var keyStrokelist = new List<string> ();
 			var keyStrokeListView = new ListView (keyStrokelist) {
 				X = 0,
@@ -126,7 +125,7 @@ namespace UICatalog.Scenarios {
 			Win.Add (processKeyLogLabel);
 
 			maxLogEntry = $"{fakeKeyPress}".Length;
-			yOffset = (Top == Application.Top ? 1 : 6);
+			yOffset = (Application.Top == Application.Top ? 1 : 6);
 			var processKeyListView = new ListView (((TestWindow)Win)._processKeyList) {
 				X = Pos.Left (processKeyLogLabel),
 				Y = Pos.Top (processKeyLogLabel) + yOffset,
@@ -144,7 +143,7 @@ namespace UICatalog.Scenarios {
 			};
 			Win.Add (processHotKeyLogLabel);
 
-			yOffset = (Top == Application.Top ? 1 : 6);
+			yOffset = (Application.Top == Application.Top ? 1 : 6);
 			var processHotKeyListView = new ListView (((TestWindow)Win)._processHotKeyList) {
 				X = Pos.Left (processHotKeyLogLabel),
 				Y = Pos.Top (processHotKeyLogLabel) + yOffset,
@@ -162,7 +161,7 @@ namespace UICatalog.Scenarios {
 			};
 			Win.Add (processColdKeyLogLabel);
 
-			yOffset = (Top == Application.Top ? 1 : 6);
+			yOffset = (Application.Top == Application.Top ? 1 : 6);
 			var processColdKeyListView = new ListView (((TestWindow)Win)._processColdKeyList) {
 				X = Pos.Left (processColdKeyLogLabel),
 				Y = Pos.Top (processColdKeyLogLabel) + yOffset,

+ 3 - 3
UICatalog/Scenarios/LabelsAsButtons.cs

@@ -59,7 +59,7 @@ namespace UICatalog.Scenarios {
 			};
 			Win.Add (colorLabelsLabel);
 
-			//With this method there is no need to call Top.Ready += () => Top.Redraw (Top.Bounds);
+			//With this method there is no need to call Application.TopReady += () => Application.TopRedraw (Top.Bounds);
 			var x = Pos.Right (colorLabelsLabel) + 2;
 			foreach (var colorScheme in Colors.ColorSchemes) {
 				var colorLabel = new Label ($"{colorScheme.Key}") {
@@ -73,7 +73,7 @@ namespace UICatalog.Scenarios {
 				Win.Add (colorLabel);
 				x += colorLabel.Text.Length + 2;
 			}
-			Top.Ready += () => Top.Redraw (Top.Bounds);
+			Application.Top.Ready += () => Application.Top.Redraw (Application.Top.Bounds);
 
 			Label Label;
 			Win.Add (Label = new Label ("A super long _Label that will probably expose a bug in clipping or wrapping of text. Will it?") {
@@ -306,7 +306,7 @@ namespace UICatalog.Scenarios {
 				}
 			};
 
-			Top.Ready += () => radioGroup.Refresh ();
+			Application.Top.Ready += () => radioGroup.Refresh ();
 		}
 	}
 }

+ 3 - 3
UICatalog/Scenarios/LineViewExample.cs

@@ -17,14 +17,14 @@ namespace UICatalog.Scenarios {
 			Win.Title = this.GetName ();
 			Win.Y = 1; // menu
 			Win.Height = Dim.Fill (1); // status bar
-			Top.LayoutSubviews ();
+			Application.Top.LayoutSubviews ();
 
 			var menu = new MenuBar (new MenuBarItem [] {
 			new MenuBarItem ("_File", new MenuItem [] {
 				new MenuItem ("_Quit", "", () => Quit()),
 			})
 			});
-			Top.Add (menu);
+			Application.Top.Add (menu);
 
 
 			Win.Add (new Label ("Regular Line") { Y = 0 });
@@ -94,7 +94,7 @@ namespace UICatalog.Scenarios {
 			var statusBar = new StatusBar (new StatusItem [] {
 				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit())
 			});
-			Top.Add (statusBar);
+			Application.Top.Add (statusBar);
 
 		}
 

+ 2 - 2
UICatalog/Scenarios/MessageBoxes.cs

@@ -156,9 +156,9 @@ namespace UICatalog.Scenarios {
 			{
 				frame.Height = Dim.Height (widthEdit) + Dim.Height (heightEdit) + Dim.Height (titleEdit) + Dim.Height (messageEdit)
 				+ Dim.Height (numButtonsEdit) + Dim.Height (defaultButtonEdit) + Dim.Height (styleRadioGroup) + 2 + Dim.Height (ckbEffect3D);
-				Top.Loaded -= Top_Loaded;
+				Application.Top.Loaded -= Top_Loaded;
 			}
-			Top.Loaded += Top_Loaded;
+			Application.Top.Loaded += Top_Loaded;
 
 			label = new Label ("Button Pressed:") {
 				X = Pos.Center (),

+ 3 - 3
UICatalog/Scenarios/MultiColouredTable.cs

@@ -16,7 +16,7 @@ namespace UICatalog.Scenarios {
 			Win.Title = this.GetName ();
 			Win.Y = 1; // menu
 			Win.Height = Dim.Fill (1); // status bar
-			Top.LayoutSubviews ();
+			Application.Top.LayoutSubviews ();
 
 			this.tableView = new TableViewColors () {
 				X = 0,
@@ -30,12 +30,12 @@ namespace UICatalog.Scenarios {
 					new MenuItem ("_Quit", "", () => Quit()),
 				}),
 			});
-			Top.Add (menu);
+			Application.Top.Add (menu);
 
 			var statusBar = new StatusBar (new StatusItem [] {
 				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
 			});
-			Top.Add (statusBar);
+			Application.Top.Add (statusBar);
 
 			Win.Add (tableView);
 

+ 5 - 6
UICatalog/Scenarios/Notepad.cs

@@ -11,11 +11,10 @@ namespace UICatalog.Scenarios {
 		private int numbeOfNewTabs = 1;
 
 		// Don't create a Window, just return the top-level view
-		public override void Init (Toplevel top, ColorScheme colorScheme)
+		public override void Init (ColorScheme colorScheme)
 		{
 			Application.Init ();
-			Top = top != null ? top : Application.Top;
-			Top.ColorScheme = Colors.Base;
+			Application.Top.ColorScheme = Colors.Base;
 		}
 
 		public override void Setup ()
@@ -30,7 +29,7 @@ namespace UICatalog.Scenarios {
 					new MenuItem ("_Quit", "", () => Quit()),
 				})
 				});
-			Top.Add (menu);
+			Application.Top.Add (menu);
 
 			tabView = new TabView () {
 				X = 0,
@@ -42,7 +41,7 @@ namespace UICatalog.Scenarios {
 			tabView.Style.ShowBorder = true;
 			tabView.ApplyStyleChanges ();
 
-			Top.Add (tabView);
+			Application.Top.Add (tabView);
 
 			var lenStatusItem = new StatusItem (Key.CharMask, "Len: ", null);
 			var statusBar = new StatusBar (new StatusItem [] {
@@ -59,7 +58,7 @@ namespace UICatalog.Scenarios {
 
 			tabView.SelectedTabChanged += (s, e) => lenStatusItem.Title = $"Len:{(e.NewTab?.View?.Text?.Length ?? 0)}";
 
-			Top.Add (statusBar);
+			Application.Top.Add (statusBar);
 
 			New ();
 		}

+ 2 - 2
UICatalog/Scenarios/ProgressBarStyles.cs

@@ -131,7 +131,7 @@ namespace UICatalog.Scenarios {
 				Application.MainLoop.Driver.Wakeup ();
 			}, null, 0, 300);
 
-			Top.Unloaded += Top_Unloaded;
+			Application.Top.Unloaded += Top_Unloaded;
 
 			void Top_Unloaded ()
 			{
@@ -143,7 +143,7 @@ namespace UICatalog.Scenarios {
 					_pulseTimer.Dispose ();
 					_pulseTimer = null;
 				}
-				Top.Unloaded -= Top_Unloaded;
+				Application.Top.Unloaded -= Top_Unloaded;
 			}
 		}
 	}

+ 1 - 1
UICatalog/Scenarios/RuneWidthGreaterThanOne.cs

@@ -16,7 +16,7 @@ namespace UICatalog.Scenarios {
 		private Window _win;
 		private string _lastRunesUsed;
 
-		public override void Init (Toplevel top, ColorScheme colorScheme)
+		public override void Init (ColorScheme colorScheme)
 		{
 			Application.Init ();
 

+ 4 - 4
UICatalog/Scenarios/Scrolling.cs

@@ -156,9 +156,9 @@ namespace UICatalog.Scenarios {
 				horizontalRuler.Text = rule.Repeat ((int)Math.Ceiling ((double)(horizontalRuler.Bounds.Width) / (double)rule.Length)) [0..(horizontalRuler.Bounds.Width)] +
 					"\n" + "|         ".Repeat ((int)Math.Ceiling ((double)(horizontalRuler.Bounds.Width) / (double)rule.Length)) [0..(horizontalRuler.Bounds.Width)];
 				verticalRuler.Text = vrule.Repeat ((int)Math.Ceiling ((double)(verticalRuler.Bounds.Height * 2) / (double)rule.Length)) [0..(verticalRuler.Bounds.Height * 2)];
-				Top.Loaded -= Top_Loaded;
+				Application.Top.Loaded -= Top_Loaded;
 			}
-			Top.Loaded += Top_Loaded;
+			Application.Top.Loaded += Top_Loaded;
 
 			var pressMeButton = new Button ("Press me!") {
 				X = 3,
@@ -313,9 +313,9 @@ namespace UICatalog.Scenarios {
 			void Top_Unloaded ()
 			{
 				pulsing = false;
-				Top.Unloaded -= Top_Unloaded;
+				Application.Top.Unloaded -= Top_Unloaded;
 			}
-			Top.Unloaded += Top_Unloaded;
+			Application.Top.Unloaded += Top_Unloaded;
 		}
 	}
 }

+ 2 - 2
UICatalog/Scenarios/SingleBackgroundWorker.cs

@@ -11,11 +11,11 @@ namespace UICatalog.Scenarios {
 	public class SingleBackgroundWorker : Scenario {
 		public override void Run ()
 		{
-			Top.Dispose ();
+			Application.Top.Dispose ();
 
 			Application.Run<MainApp> ();
 
-			Top.Dispose ();
+			Application.Top.Dispose ();
 		}
 
 		public class MainApp : Toplevel {

+ 3 - 3
UICatalog/Scenarios/SyntaxHighlighting.cs

@@ -21,7 +21,7 @@ namespace UICatalog.Scenarios {
 			Win.Title = this.GetName ();
 			Win.Y = 1; // menu
 			Win.Height = Dim.Fill (1); // status bar
-			Top.LayoutSubviews ();
+			Application.Top.LayoutSubviews ();
 
 			var menu = new MenuBar (new MenuBarItem [] {
 			new MenuBarItem ("_File", new MenuItem [] {
@@ -29,7 +29,7 @@ namespace UICatalog.Scenarios {
 				new MenuItem ("_Quit", "", () => Quit()),
 			})
 			});
-			Top.Add (menu);
+			Application.Top.Add (menu);
 
 			textView = new SqlTextView () {
 				X = 0,
@@ -49,7 +49,7 @@ namespace UICatalog.Scenarios {
 			});
 
 
-			Top.Add (statusBar);
+			Application.Top.Add (statusBar);
 		}
 
 		private void WordWrap ()

+ 3 - 7
UICatalog/Scenarios/TabViewExample.cs

@@ -24,7 +24,7 @@ namespace UICatalog.Scenarios {
 			Win.Title = this.GetName ();
 			Win.Y = 1; // menu
 			Win.Height = Dim.Fill (1); // status bar
-			Top.LayoutSubviews ();
+			Application.Top.LayoutSubviews ();
 
 			var menu = new MenuBar (new MenuBarItem [] {
 				new MenuBarItem ("_File", new MenuItem [] {
@@ -50,7 +50,7 @@ namespace UICatalog.Scenarios {
 
 					})
 				});
-			Top.Add (menu);
+			Application.Top.Add (menu);
 
 			tabView = new TabView () {
 				X = 0,
@@ -85,7 +85,6 @@ namespace UICatalog.Scenarios {
 				Height = Dim.Fill (),
 			};
 
-
 			frameRight.Add (new TextView () {
 				Text = "This demos the tabs control\nSwitch between tabs using cursor keys",
 				Width = Dim.Fill (),
@@ -94,8 +93,6 @@ namespace UICatalog.Scenarios {
 
 			Win.Add (frameRight);
 
-
-
 			var frameBelow = new FrameView ("Bottom Frame") {
 				X = 0,
 				Y = Pos.Bottom (tabView),
@@ -103,7 +100,6 @@ namespace UICatalog.Scenarios {
 				Height = Dim.Fill (),
 			};
 
-
 			frameBelow.Add (new TextView () {
 				Text = "This frame exists to check you can still tab here\nand that the tab control doesn't overspill it's bounds",
 				Width = Dim.Fill (),
@@ -115,7 +111,7 @@ namespace UICatalog.Scenarios {
 			var statusBar = new StatusBar (new StatusItem [] {
 				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
 			});
-			Top.Add (statusBar);
+			Application.Top.Add (statusBar);
 		}
 
 		private void AddBlankTab ()

+ 4 - 4
UICatalog/Scenarios/TableEditor.cs

@@ -38,7 +38,7 @@ namespace UICatalog.Scenarios {
 			Win.Title = this.GetName();
 			Win.Y = 1; // menu
 			Win.Height = Dim.Fill (1); // status bar
-			Top.LayoutSubviews ();
+			Application.Top.LayoutSubviews ();
 
 			this.tableView = new TableView () {
 				X = 0,
@@ -78,9 +78,9 @@ namespace UICatalog.Scenarios {
 					new MenuItem ("_Set All MinAcceptableWidth=1", "",SetMinAcceptableWidthToOne),
 				}),
 			});
-		
 
-		Top.Add (menu);
+
+			Application.Top.Add (menu);
 
 			var statusBar = new StatusBar (new StatusItem [] {
 				new StatusItem(Key.F2, "~F2~ OpenExample", () => OpenExample(true)),
@@ -88,7 +88,7 @@ namespace UICatalog.Scenarios {
 				new StatusItem(Key.F4, "~F4~ OpenSimple", () => OpenSimple(true)),
 				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
 			});
-			Top.Add (statusBar);
+			Application.Top.Add (statusBar);
 
 			Win.Add (tableView);
 

+ 1 - 1
UICatalog/Scenarios/TextFormatterDemo.cs

@@ -42,7 +42,7 @@ namespace UICatalog.Scenarios {
 			blockText.Text = ustring.Make (block.ToString ()); // .Replace(" ", "\u00A0"); // \u00A0 is 'non-breaking space
 			Win.Add (blockText);
 
-			var unicodeCheckBox = new CheckBox ("Unicode", Top.HotKeySpecifier == (Rune)' ') {
+			var unicodeCheckBox = new CheckBox ("Unicode", Application.Top.HotKeySpecifier == (Rune)' ') {
 				X = 0,
 				Y = Pos.Bottom (blockText) + 1,
 			};

+ 2 - 2
UICatalog/Scenarios/TextViewAutocompletePopup.cs

@@ -33,7 +33,7 @@ namespace UICatalog.Scenarios {
 					new MenuItem ("_Quit", "", () => Quit())
 				})
 			});
-			Top.Add (menu);
+			Application.Top.Add (menu);
 
 			textViewTopLeft = new TextView () {
 				Width = width,
@@ -89,7 +89,7 @@ namespace UICatalog.Scenarios {
 				siMultiline = new StatusItem(Key.Null, "", null),
 				siWrap = new StatusItem(Key.Null, "", null)
 			});
-			Top.Add (statusBar);
+			Application.Top.Add (statusBar);
 
 			Win.LayoutStarted += Win_LayoutStarted;
 		}

+ 2 - 2
UICatalog/Scenarios/Threading.cs

@@ -96,9 +96,9 @@ namespace UICatalog.Scenarios {
 			void Top_Loaded ()
 			{
 				_btnActionCancel.SetFocus ();
-				Top.Loaded -= Top_Loaded;
+				Application.Top.Loaded -= Top_Loaded;
 			}
-			Top.Loaded += Top_Loaded;
+			Application.Top.Loaded += Top_Loaded;
 		}
 
 		private async void LoadData ()

+ 3 - 3
UICatalog/Scenarios/TreeUseCases.cs

@@ -17,7 +17,7 @@ namespace UICatalog.Scenarios {
 			Win.Title = this.GetName ();
 			Win.Y = 1; // menu
 			Win.Height = Dim.Fill (1); // status bar
-			Top.LayoutSubviews ();
+			Application.Top.LayoutSubviews ();
 
 			var menu = new MenuBar (new MenuBarItem [] {
 				new MenuBarItem ("_File", new MenuItem [] {
@@ -31,13 +31,13 @@ namespace UICatalog.Scenarios {
 				}),
 			});
 
-			Top.Add (menu);
+			Application.Top.Add (menu);
 
 			var statusBar = new StatusBar (new StatusItem [] {
 				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
 			});
 
-			Top.Add (statusBar);
+			Application.Top.Add (statusBar);
 
 			// Start with the most basic use case
 			LoadSimpleNodes ();

+ 3 - 3
UICatalog/Scenarios/TreeViewFileSystem.cs

@@ -35,7 +35,7 @@ namespace UICatalog.Scenarios {
 			Win.Title = this.GetName ();
 			Win.Y = 1; // menu
 			Win.Height = Dim.Fill (1); // status bar
-			Top.LayoutSubviews ();
+			Application.Top.LayoutSubviews ();
 
 			var menu = new MenuBar (new MenuBarItem [] {
 				new MenuBarItem ("_File", new MenuItem [] {
@@ -60,12 +60,12 @@ namespace UICatalog.Scenarios {
 					miMultiSelect = new MenuItem ("_MultiSelect", "", () => SetMultiSelect()){Checked = true, CheckType = MenuItemCheckStyle.Checked},
 				}),
 			});
-			Top.Add (menu);
+			Application.Top.Add (menu);
 
 			var statusBar = new StatusBar (new StatusItem [] {
 				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
 			});
-			Top.Add (statusBar);
+			Application.Top.Add (statusBar);
 
 			var lblFiles = new Label ("File Tree:") {
 				X = 0,

+ 2 - 2
UICatalog/Scenarios/Unicode.cs

@@ -34,14 +34,14 @@ namespace UICatalog.Scenarios {
 					new MenuItem ("_Paste", "", null)
 				})
 			});
-			Top.Add (menu);
+			Application.Top.Add (menu);
 
 			var statusBar = new StatusBar (new StatusItem [] {
 				new StatusItem (Key.CtrlMask | Key.Q, "~^Q~ Выход", () => Application.RequestStop()),
 				new StatusItem (Key.Unknown, "~F2~ Создать", null),
 				new StatusItem(Key.Unknown, "~F3~ Со_хранить", null),
 			});
-			Top.Add (statusBar);
+			Application.Top.Add (statusBar);
 
 			var label = new Label ("Label:") { X = 0, Y = 1 };
 			Win.Add (label);

+ 3 - 11
UICatalog/Scenarios/WindowsAndFrameViews.cs

@@ -6,13 +6,6 @@ namespace UICatalog.Scenarios {
 	[ScenarioMetadata (Name: "Windows & FrameViews", Description: "Shows Windows, sub-Windows, and FrameViews.")]
 	[ScenarioCategory ("Layout")]
 	public class WindowsAndFrameViews : Scenario {
-		public override void Init (Toplevel top, ColorScheme colorScheme)
-		{
-			Application.Init ();
-
-			Top = top != null ? top : Application.Top;
-		}
-
 		public override void RequestStop ()
 		{
 			base.RequestStop ();
@@ -67,7 +60,7 @@ namespace UICatalog.Scenarios {
 				Y = Pos.AnchorEnd (1),
 				ColorScheme = Colors.Error
 			});
-			Top.Add (Win);
+			Application.Top.Add (Win);
 			listWin.Add (Win);
 
 			for (var i = 0; i < 3; i++) {
@@ -114,11 +107,10 @@ namespace UICatalog.Scenarios {
 				});
 				win.Add (frameView);
 
-				Top.Add (win);
+				Application.Top.Add (win);
 				listWin.Add (win);
 			}
 
-
 			FrameView frame = null;
 			frame = new FrameView ($"This is a FrameView") {
 				X = margin,
@@ -176,7 +168,7 @@ namespace UICatalog.Scenarios {
 
 			frame.Add (subFrameViewofFV);
 
-			Top.Add (frame);
+			Application.Top.Add (frame);
 			listWin.Add (frame);
 		}
 	}

+ 4 - 5
UICatalog/Scenarios/WizardAsView.cs

@@ -10,10 +10,9 @@ namespace UICatalog.Scenarios {
 	[ScenarioCategory ("Wizards")]
 	public class WizardAsView : Scenario {
 
-		public override void Init (Toplevel top, ColorScheme colorScheme)
+		public override void Init (ColorScheme colorScheme)
 		{
 			Application.Init ();
-			Top = Application.Top;
 
 			var menu = new MenuBar (new MenuBarItem [] {
 				new MenuBarItem ("_File", new MenuItem [] {
@@ -22,7 +21,7 @@ namespace UICatalog.Scenarios {
 					new MenuItem ("_Shutdown Server...", "", () => MessageBox.Query ("Wizaard", "Are you sure you want to cancel setup and shutdown?", "Ok", "Cancel")),
 				})
 			});
-			Top.Add (menu);
+			Application.Top.Add (menu);
 
 			// No need for a Title because the border is disabled
 			var wizard = new Wizard () {
@@ -94,8 +93,8 @@ namespace UICatalog.Scenarios {
 			wizard.AddStep (lastStep);
 			lastStep.HelpText = "The wizard is complete!\n\nPress the Finish button to continue.\n\nPressing Esc will cancel.";
 
-			Top.Add (wizard);
-			Application.Run (Top);
+			Application.Top.Add (wizard);
+			Application.Run (Application.Top);
 		}
 
 		public override void Run ()

+ 2 - 2
UICatalog/Scenarios/Wizards.cs

@@ -73,9 +73,9 @@ namespace UICatalog.Scenarios {
 			void Top_Loaded ()
 			{
 				frame.Height = Dim.Height (widthEdit) + Dim.Height (heightEdit) + Dim.Height (titleEdit) + 2;
-				Top.Loaded -= Top_Loaded;
+				Application.Top.Loaded -= Top_Loaded;
 			}
-			Top.Loaded += Top_Loaded;
+			Application.Top.Loaded += Top_Loaded;
 
 			label = new Label ("Action:") {
 				X = Pos.Center (),

+ 401 - 390
UICatalog/UICatalog.cs

@@ -44,35 +44,7 @@ namespace UICatalog {
 	/// <summary>
 	/// UI Catalog is a comprehensive sample app and scenario library for <see cref="Terminal.Gui"/>
 	/// </summary>
-	public class UICatalogApp {
-		private static int _nameColumnWidth;
-		private static FrameView _leftPane;
-		private static List<string> _categories;
-		private static ListView _categoryListView;
-		private static FrameView _rightPane;
-		private static List<Scenario> _scenarios;
-		private static ListView _scenarioListView;
-		private static StatusBar _statusBar;
-		private static StatusItem _capslock;
-		private static StatusItem _numlock;
-		private static StatusItem _scrolllock;
-
-		// If set, holds the scenario the user selected
-		private static Scenario _selectedScenario = null;
-		
-		private static bool _useSystemConsole = false;
-		private static ConsoleDriver.DiagnosticFlags _diagnosticFlags;
-		private static bool _heightAsBuffer = false;
-		private static bool _isFirstRunning = true;
-
-		// When a scenario is run, the main app is killed. These items
-		// are therefore cached so that when the scenario exits the
-		// main app UI can be restored to previous state
-		private static int _cachedScenarioIndex = 0;
-		private static int _cachedCategoryIndex = 0;
-
-		private static StringBuilder _aboutMessage;
-
+	class UICatalogApp {
 		static void Main (string [] args)
 		{
 			Console.OutputEncoding = Encoding.Default;
@@ -82,17 +54,22 @@ namespace UICatalog {
 			}
 
 			_scenarios = Scenario.GetScenarios ();
+			_categories = Scenario.GetAllCategories ();
+			_nameColumnWidth = _scenarios.OrderByDescending (s => s.GetName ().Length).FirstOrDefault ().GetName ().Length;
 
 			if (args.Length > 0 && args.Contains ("-usc")) {
 				_useSystemConsole = true;
 				args = args.Where (val => val != "-usc").ToArray ();
 			}
+
+			// If a Scenario name has been provided on the commandline
+			// run it and exit when done.
 			if (args.Length > 0) {
 				var item = _scenarios.FindIndex (s => s.GetName ().Equals (args [0], StringComparison.OrdinalIgnoreCase));
 				_selectedScenario = (Scenario)Activator.CreateInstance (_scenarios [item].GetType ());
 				Application.UseSystemConsole = _useSystemConsole;
 				Application.Init ();
-				_selectedScenario.Init (Application.Top, _colorScheme);
+				_selectedScenario.Init (_colorScheme);
 				_selectedScenario.Setup ();
 				_selectedScenario.Run ();
 				_selectedScenario = null;
@@ -113,43 +90,19 @@ namespace UICatalog {
 			_aboutMessage.AppendLine (@"https://github.com/gui-cs/Terminal.Gui");
 
 			Scenario scenario;
-			while ((scenario = SelectScenario ()) != null) {
-#if DEBUG_IDISPOSABLE
-				// Validate there are no outstanding Responder-based instances 
-				// after a scenario was selected to run. This proves the main UI Catalog
-				// 'app' closed cleanly.
-				foreach (var inst in Responder.Instances) {
-					Debug.Assert (inst.WasDisposed);
-				}
-				Responder.Instances.Clear ();
-#endif
-
-				scenario.Init (Application.Top, _colorScheme);
+			while ((scenario = RunUICatalogTopLevel ()) != null) {
+				VerifyObjectsWereDisposed ();
+				scenario.Init (_colorScheme);
 				scenario.Setup ();
 				scenario.Run ();
 
 				// This call to Application.Shutdown brackets the Application.Init call
-				// made by Scenario.Init()
+				// made by Scenario.Init() above
 				Application.Shutdown ();
 
-#if DEBUG_IDISPOSABLE
-				// After the scenario runs, validate all Responder-based instances
-				// were disposed. This proves the scenario 'app' closed cleanly.
-				foreach (var inst in Responder.Instances) {
-					Debug.Assert (inst.WasDisposed);
-				}
-				Responder.Instances.Clear ();
-#endif
-			}
-
-#if DEBUG_IDISPOSABLE
-			// This proves that when the user exited the UI Catalog app
-			// it cleaned up properly.
-			foreach (var inst in Responder.Instances) {
-				Debug.Assert (inst.WasDisposed);
+				VerifyObjectsWereDisposed ();
 			}
-			Responder.Instances.Clear ();
-#endif
+			VerifyObjectsWereDisposed ();
 		}
 
 		/// <summary>
@@ -158,389 +111,447 @@ namespace UICatalog {
 		/// When the Scenario exits, this function exits.
 		/// </summary>
 		/// <returns></returns>
-		private static Scenario SelectScenario ()
+		static Scenario RunUICatalogTopLevel ()
 		{
 			Application.UseSystemConsole = _useSystemConsole;
-			Application.Init ();
-			if (_colorScheme == null) {
-				// `Colors` is not initilized until the ConsoleDriver is loaded by 
-				// Application.Init. Set it only the first time though so it is
-				// preserved between running multiple Scenarios
-				_colorScheme = Colors.Base;
-			}
-			Application.HeightAsBuffer = _heightAsBuffer;
-
-			var menu = new MenuBar (new MenuBarItem [] {
-				new MenuBarItem ("_File", new MenuItem [] {
-					new MenuItem ("_Quit", "Quit UI Catalog", () => Application.RequestStop(), null, null, Key.Q | Key.CtrlMask)
-				}),
-				new MenuBarItem ("_Color Scheme", CreateColorSchemeMenuItems()),
-				new MenuBarItem ("Diag_nostics", CreateDiagnosticMenuItems()),
-				new MenuBarItem ("_Help", new MenuItem [] {
-					new MenuItem ("_gui.cs API Overview", "", () => OpenUrl ("https://gui-cs.github.io/Terminal.Gui/articles/overview.html"), null, null, Key.F1),
-					new MenuItem ("gui.cs _README", "", () => OpenUrl ("https://github.com/gui-cs/Terminal.Gui"), null, null, Key.F2),
-					new MenuItem ("_About...",
-						"About UI Catalog", () =>  MessageBox.Query ("About UI Catalog", _aboutMessage.ToString(), "_Ok"), null, null, Key.CtrlMask | Key.A),
-				}),
-			});
-
-			_leftPane = new FrameView ("Categories") {
-				X = 0,
-				Y = 1, // for menu
-				Width = 25,
-				Height = Dim.Fill (1),
-				CanFocus = true,
-				Shortcut = Key.CtrlMask | Key.C
-			};
-			_leftPane.Title = $"{_leftPane.Title} ({_leftPane.ShortcutTag})";
-			_leftPane.ShortcutAction = () => _leftPane.SetFocus ();
-
-			_categories = Scenario.GetAllCategories ();
-			_categoryListView = new ListView (_categories) {
-				X = 0,
-				Y = 0,
-				Width = Dim.Fill (0),
-				Height = Dim.Fill (0),
-				AllowsMarking = false,
-				CanFocus = true,
-			};
-			_categoryListView.OpenSelectedItem += (a) => {
-				_rightPane.SetFocus ();
-			};
-			_categoryListView.SelectedItemChanged += CategoryListView_SelectedChanged;
-			_leftPane.Add (_categoryListView);
-
-			_rightPane = new FrameView ("Scenarios") {
-				X = 25,
-				Y = 1, // for menu
-				Width = Dim.Fill (),
-				Height = Dim.Fill (1),
-				CanFocus = true,
-				Shortcut = Key.CtrlMask | Key.S
-			};
-			_rightPane.Title = $"{_rightPane.Title} ({_rightPane.ShortcutTag})";
-			_rightPane.ShortcutAction = () => _rightPane.SetFocus ();
-
-			_nameColumnWidth = _scenarios.OrderByDescending (s => s.GetName ().Length).FirstOrDefault ().GetName ().Length;
-
-			_scenarioListView = new ListView () {
-				X = 0,
-				Y = 0,
-				Width = Dim.Fill (0),
-				Height = Dim.Fill (0),
-				AllowsMarking = false,
-				CanFocus = true,
-			};
-
-			_scenarioListView.OpenSelectedItem += _scenarioListView_OpenSelectedItem;
-			_rightPane.Add (_scenarioListView);
-
-			_capslock = new StatusItem (Key.CharMask, "Caps", null);
-			_numlock = new StatusItem (Key.CharMask, "Num", null);
-			_scrolllock = new StatusItem (Key.CharMask, "Scroll", null);
-
-			_statusBar = new StatusBar () {
-				Visible = true,
-			};
-			_statusBar.Items = new StatusItem [] {
-				_capslock,
-				_numlock,
-				_scrolllock,
-				new StatusItem(Key.Q | Key.CtrlMask, "~CTRL-Q~ Quit", () => {
-					if (_selectedScenario is null){
-						// This causes GetScenarioToRun to return null
-						_selectedScenario = null;
-						Application.RequestStop();
-					} else {
-						_selectedScenario.RequestStop();
-					}
-				}),
-				new StatusItem(Key.F10, "~F10~ Hide/Show Status Bar", () => {
-					_statusBar.Visible = !_statusBar.Visible;
-					_leftPane.Height = Dim.Fill(_statusBar.Visible ? 1 : 0);
-					_rightPane.Height = Dim.Fill(_statusBar.Visible ? 1 : 0);
-					Application.Top.LayoutSubviews();
-					Application.Top.SetChildNeedsDisplay();
-				}),
-				new StatusItem (Key.CharMask, Application.Driver.GetType ().Name, null),
-			};
-
-			Application.Top.ColorScheme = _colorScheme;
-			Application.Top.KeyDown += KeyDownHandler;
-			Application.Top.Add (menu);
-			Application.Top.Add (_leftPane);
-			Application.Top.Add (_rightPane);
-			Application.Top.Add (_statusBar);
-
-			void TopHandler ()
-			{
-				if (_selectedScenario != null) {
-					_selectedScenario = null;
-					_isFirstRunning = false;
-				}
-				if (!_isFirstRunning) {
-					_rightPane.SetFocus ();
-				}
-				Application.Top.Loaded -= TopHandler;
-			}
-			Application.Top.Loaded += TopHandler;
-
-			// Restore previous selections
-			_categoryListView.SelectedItem = _cachedCategoryIndex;
-			_scenarioListView.SelectedItem = _cachedScenarioIndex;
 
 			// Run UI Catalog UI. When it exits, if _selectedScenario is != null then
 			// a Scenario was selected. Otherwise, the user wants to exit UI Catalog.
-			Application.Run (Application.Top);
+			Application.Run<UICatalogTopLevel> ();
 			Application.Shutdown ();
 
 			return _selectedScenario;
 		}
 
+		static List<Scenario> _scenarios;
+		static List<string> _categories;
+		static int _nameColumnWidth;
+		// When a scenario is run, the main app is killed. These items
+		// are therefore cached so that when the scenario exits the
+		// main app UI can be restored to previous state
+		static int _cachedScenarioIndex = 0;
+		static int _cachedCategoryIndex = 0;
+		static StringBuilder _aboutMessage;
+
+		// If set, holds the scenario the user selected
+		static Scenario _selectedScenario = null;
+
+		static bool _useSystemConsole = false;
+		static ConsoleDriver.DiagnosticFlags _diagnosticFlags;
+		static bool _heightAsBuffer = false;
+		static bool _isFirstRunning = true;
+		static ColorScheme _colorScheme;
 
 		/// <summary>
-		/// Launches the selected scenario, setting the global _selectedScenario
+		/// This is the main UI Catalog app view. It is run fresh when the app loads (if a Scenario has not been passed on 
+		/// the command line) and each time a Scenario ends.
 		/// </summary>
-		/// <param name="e"></param>
-		private static void _scenarioListView_OpenSelectedItem (EventArgs e)
-		{
-			if (_selectedScenario is null) {
-				// Save selected item state
-				_cachedCategoryIndex = _categoryListView.SelectedItem;
-				_cachedScenarioIndex = _scenarioListView.SelectedItem;
-				// Create new instance of scenario (even though Scenarios contains instances)
-				_selectedScenario = (Scenario)Activator.CreateInstance (_scenarioListView.Source.ToList () [_scenarioListView.SelectedItem].GetType ());
-
-				// Tell the main app to stop
-				Application.RequestStop ();
-			}
-		}
+		class UICatalogTopLevel : Toplevel {
+			public MenuItem miIsMouseDisabled;
+			public MenuItem miHeightAsBuffer;
+	    
+			public FrameView LeftPane;
+			public ListView CategoryListView;
+			public FrameView RightPane;
+			public ListView ScenarioListView;
+	    
+			public StatusItem Capslock;
+			public StatusItem Numlock;
+			public StatusItem Scrolllock;
+			public StatusItem DriverName;
+
+			public UICatalogTopLevel ()
+			{
+				ColorScheme = _colorScheme;
+				MenuBar = new MenuBar (new MenuBarItem [] {
+					new MenuBarItem ("_File", new MenuItem [] {
+						new MenuItem ("_Quit", "Quit UI Catalog", () => RequestStop(), null, null, Key.Q | Key.CtrlMask)
+					}),
+					new MenuBarItem ("_Color Scheme", CreateColorSchemeMenuItems()),
+					new MenuBarItem ("Diag_nostics", CreateDiagnosticMenuItems()),
+					new MenuBarItem ("_Help", new MenuItem [] {
+						new MenuItem ("_gui.cs API Overview", "", () => OpenUrl ("https://gui-cs.github.io/Terminal.Gui/articles/overview.html"), null, null, Key.F1),
+						new MenuItem ("gui.cs _README", "", () => OpenUrl ("https://github.com/gui-cs/Terminal.Gui"), null, null, Key.F2),
+						new MenuItem ("_About...",
+							"About UI Catalog", () =>  MessageBox.Query ("About UI Catalog", _aboutMessage.ToString(), "_Ok"), null, null, Key.CtrlMask | Key.A),
+					}),
+				});
+
+				Capslock = new StatusItem (Key.CharMask, "Caps", null);
+				Numlock = new StatusItem (Key.CharMask, "Num", null);
+				Scrolllock = new StatusItem (Key.CharMask, "Scroll", null);
+				DriverName = new StatusItem (Key.CharMask, "Driver:", null);
+
+				StatusBar = new StatusBar () {
+					Visible = true,
+				};
+				StatusBar.Items = new StatusItem [] {
+					Capslock,
+					Numlock,
+					Scrolllock,
+					new StatusItem(Key.Q | Key.CtrlMask, "~CTRL-Q~ Quit", () => {
+						if (_selectedScenario is null){
+							// This causes GetScenarioToRun to return null
+							_selectedScenario = null;
+							RequestStop();
+						} else {
+							_selectedScenario.RequestStop();
+						}
+					}),
+					new StatusItem(Key.F10, "~F10~ Hide/Show Status Bar", () => {
+						StatusBar.Visible = !StatusBar.Visible;
+						LeftPane.Height = Dim.Fill(StatusBar.Visible ? 1 : 0);
+						RightPane.Height = Dim.Fill(StatusBar.Visible ? 1 : 0);
+						LayoutSubviews();
+						SetChildNeedsDisplay();
+					}),
+					DriverName,
+				};
 
-		static List<MenuItem []> CreateDiagnosticMenuItems ()
-		{
-			List<MenuItem []> menuItems = new List<MenuItem []> ();
-			menuItems.Add (CreateDiagnosticFlagsMenuItems ());
-			menuItems.Add (new MenuItem [] { null });
-			menuItems.Add (CreateSizeStyle ());
-			menuItems.Add (CreateDisabledEnabledMouse ());
-			menuItems.Add (CreateKeybindings ());
-			return menuItems;
-		}
+				LeftPane = new FrameView ("Categories") {
+					X = 0,
+					Y = 1, // for menu
+					Width = 25,
+					Height = Dim.Fill (1),
+					CanFocus = true,
+					Shortcut = Key.CtrlMask | Key.C
+				};
+				LeftPane.Title = $"{LeftPane.Title} ({LeftPane.ShortcutTag})";
+				LeftPane.ShortcutAction = () => LeftPane.SetFocus ();
+
+				CategoryListView = new ListView (_categories) {
+					X = 0,
+					Y = 0,
+					Width = Dim.Fill (0),
+					Height = Dim.Fill (0),
+					AllowsMarking = false,
+					CanFocus = true,
+				};
+				CategoryListView.OpenSelectedItem += (a) => {
+					RightPane.SetFocus ();
+				};
+				CategoryListView.SelectedItemChanged += CategoryListView_SelectedChanged;
+				LeftPane.Add (CategoryListView);
+
+				RightPane = new FrameView ("Scenarios") {
+					X = 25,
+					Y = 1, // for menu
+					Width = Dim.Fill (),
+					Height = Dim.Fill (1),
+					CanFocus = true,
+					Shortcut = Key.CtrlMask | Key.S
+				};
+				RightPane.Title = $"{RightPane.Title} ({RightPane.ShortcutTag})";
+				RightPane.ShortcutAction = () => RightPane.SetFocus ();
+
+				ScenarioListView = new ListView () {
+					X = 0,
+					Y = 0,
+					Width = Dim.Fill (0),
+					Height = Dim.Fill (0),
+					AllowsMarking = false,
+					CanFocus = true,
+				};
 
-		private static MenuItem [] CreateDisabledEnabledMouse ()
-		{
-			List<MenuItem> menuItems = new List<MenuItem> ();
-			var item = new MenuItem ();
-			item.Title = "_Disable Mouse";
-			item.Shortcut = Key.CtrlMask | Key.AltMask | (Key)item.Title.ToString ().Substring (1, 1) [0];
-			item.CheckType |= MenuItemCheckStyle.Checked;
-			item.Checked = Application.IsMouseDisabled;
-			item.Action += () => {
-				item.Checked = Application.IsMouseDisabled = !item.Checked;
-			};
-			menuItems.Add (item);
-
-			return menuItems.ToArray ();
-		}
-		private static MenuItem [] CreateKeybindings ()
-		{
+				ScenarioListView.OpenSelectedItem += ScenarioListView_OpenSelectedItem;
+				RightPane.Add (ScenarioListView);
 
-			List<MenuItem> menuItems = new List<MenuItem> ();
-			var item = new MenuItem ();
-			item.Title = "_Key Bindings";
-			item.Help = "Change which keys do what";
-			item.Action += () => {
-				var dlg = new KeyBindingsDialog ();
-				Application.Run (dlg);
-			};
+				KeyDown += KeyDownHandler;
+				Add (MenuBar);
+				Add (LeftPane);
+				Add (RightPane);
+				Add (StatusBar);
 
-			menuItems.Add (null);
-			menuItems.Add (item);
+				Loaded += LoadedHandler;
 
-			return menuItems.ToArray ();
-		}
+				// Restore previous selections
+				CategoryListView.SelectedItem = _cachedCategoryIndex;
+				ScenarioListView.SelectedItem = _cachedScenarioIndex;
+			}
 
-		static MenuItem [] CreateSizeStyle ()
-		{
-			List<MenuItem> menuItems = new List<MenuItem> ();
-			var item = new MenuItem ();
-			item.Title = "_Height As Buffer";
-			item.Shortcut = Key.CtrlMask | Key.AltMask | (Key)item.Title.ToString ().Substring (1, 1) [0];
-			item.CheckType |= MenuItemCheckStyle.Checked;
-			item.Checked = Application.HeightAsBuffer;
-			item.Action += () => {
-				item.Checked = !item.Checked;
-				_heightAsBuffer = item.Checked;
+			void LoadedHandler ()
+			{
 				Application.HeightAsBuffer = _heightAsBuffer;
-			};
-			menuItems.Add (item);
 
-			return menuItems.ToArray ();
-		}
+				if (_colorScheme == null) {
+					ColorScheme = _colorScheme = Colors.Base;
+				}
 
-		static MenuItem [] CreateDiagnosticFlagsMenuItems ()
-		{
-			const string OFF = "Diagnostics: _Off";
-			const string FRAME_RULER = "Diagnostics: Frame _Ruler";
-			const string FRAME_PADDING = "Diagnostics: _Frame Padding";
-			var index = 0;
+				miIsMouseDisabled.Checked = Application.IsMouseDisabled;
+				miHeightAsBuffer.Checked = Application.HeightAsBuffer;
+				DriverName.Title = $"Driver: {Driver.GetType ().Name}";
 
-			List<MenuItem> menuItems = new List<MenuItem> ();
-			foreach (Enum diag in Enum.GetValues (_diagnosticFlags.GetType ())) {
-				var item = new MenuItem ();
-				item.Title = GetDiagnosticsTitle (diag);
-				item.Shortcut = Key.AltMask + index.ToString () [0];
-				index++;
-				item.CheckType |= MenuItemCheckStyle.Checked;
-				if (GetDiagnosticsTitle (ConsoleDriver.DiagnosticFlags.Off) == item.Title) {
-					item.Checked = (_diagnosticFlags & (ConsoleDriver.DiagnosticFlags.FramePadding
-					| ConsoleDriver.DiagnosticFlags.FrameRuler)) == 0;
-				} else {
-					item.Checked = _diagnosticFlags.HasFlag (diag);
+				if (_selectedScenario != null) {
+					_selectedScenario = null;
+					_isFirstRunning = false;
 				}
-				item.Action += () => {
-					var t = GetDiagnosticsTitle (ConsoleDriver.DiagnosticFlags.Off);
-					if (item.Title == t && !item.Checked) {
-						_diagnosticFlags &= ~(ConsoleDriver.DiagnosticFlags.FramePadding | ConsoleDriver.DiagnosticFlags.FrameRuler);
-						item.Checked = true;
-					} else if (item.Title == t && item.Checked) {
-						_diagnosticFlags |= (ConsoleDriver.DiagnosticFlags.FramePadding | ConsoleDriver.DiagnosticFlags.FrameRuler);
-						item.Checked = false;
-					} else {
-						var f = GetDiagnosticsEnumValue (item.Title);
-						if (_diagnosticFlags.HasFlag (f)) {
-							SetDiagnosticsFlag (f, false);
-						} else {
-							SetDiagnosticsFlag (f, true);
-						}
-					}
-					foreach (var menuItem in menuItems) {
-						if (menuItem.Title == t) {
-							menuItem.Checked = !_diagnosticFlags.HasFlag (ConsoleDriver.DiagnosticFlags.FrameRuler)
-								&& !_diagnosticFlags.HasFlag (ConsoleDriver.DiagnosticFlags.FramePadding);
-						} else if (menuItem.Title != t) {
-							menuItem.Checked = _diagnosticFlags.HasFlag (GetDiagnosticsEnumValue (menuItem.Title));
-						}
-					}
-					ConsoleDriver.Diagnostics = _diagnosticFlags;
-					Application.Top.SetNeedsDisplay ();
-				};
-				menuItems.Add (item);
+				if (!_isFirstRunning) {
+					RightPane.SetFocus ();
+				}
+				Loaded -= LoadedHandler;
 			}
-			return menuItems.ToArray ();
 
-			string GetDiagnosticsTitle (Enum diag)
+			/// <summary>
+			/// Launches the selected scenario, setting the global _selectedScenario
+			/// </summary>
+			/// <param name="e"></param>
+			void ScenarioListView_OpenSelectedItem (EventArgs e)
 			{
-				switch (Enum.GetName (_diagnosticFlags.GetType (), diag)) {
-				case "Off":
-					return OFF;
-				case "FrameRuler":
-					return FRAME_RULER;
-				case "FramePadding":
-					return FRAME_PADDING;
+				if (_selectedScenario is null) {
+					// Save selected item state
+					_cachedCategoryIndex = CategoryListView.SelectedItem;
+					_cachedScenarioIndex = ScenarioListView.SelectedItem;
+					// Create new instance of scenario (even though Scenarios contains instances)
+					_selectedScenario = (Scenario)Activator.CreateInstance (ScenarioListView.Source.ToList () [ScenarioListView.SelectedItem].GetType ());
+
+					// Tell the main app to stop
+					Application.RequestStop ();
 				}
-				return "";
 			}
 
-			Enum GetDiagnosticsEnumValue (ustring title)
+			List<MenuItem []> CreateDiagnosticMenuItems ()
 			{
-				switch (title.ToString ()) {
-				case FRAME_RULER:
-					return ConsoleDriver.DiagnosticFlags.FrameRuler;
-				case FRAME_PADDING:
-					return ConsoleDriver.DiagnosticFlags.FramePadding;
-				}
-				return null;
+				List<MenuItem []> menuItems = new List<MenuItem []> ();
+				menuItems.Add (CreateDiagnosticFlagsMenuItems ());
+				menuItems.Add (new MenuItem [] { null });
+				menuItems.Add (CreateHeightAsBufferMenuItems ());
+				menuItems.Add (CreateDisabledEnabledMouseItems ());
+				menuItems.Add (CreateKeybindingsMenuItems ());
+				return menuItems;
 			}
 
-			void SetDiagnosticsFlag (Enum diag, bool add)
+			MenuItem [] CreateDisabledEnabledMouseItems ()
 			{
-				switch (diag) {
-				case ConsoleDriver.DiagnosticFlags.FrameRuler:
-					if (add) {
-						_diagnosticFlags |= ConsoleDriver.DiagnosticFlags.FrameRuler;
-					} else {
-						_diagnosticFlags &= ~ConsoleDriver.DiagnosticFlags.FrameRuler;
-					}
-					break;
-				case ConsoleDriver.DiagnosticFlags.FramePadding:
-					if (add) {
-						_diagnosticFlags |= ConsoleDriver.DiagnosticFlags.FramePadding;
-					} else {
-						_diagnosticFlags &= ~ConsoleDriver.DiagnosticFlags.FramePadding;
-					}
-					break;
-				default:
-					_diagnosticFlags = default;
-					break;
-				}
+				List<MenuItem> menuItems = new List<MenuItem> ();
+				miIsMouseDisabled = new MenuItem ();
+				miIsMouseDisabled.Title = "_Disable Mouse";
+				miIsMouseDisabled.Shortcut = Key.CtrlMask | Key.AltMask | (Key)miIsMouseDisabled.Title.ToString ().Substring (1, 1) [0];
+				miIsMouseDisabled.CheckType |= MenuItemCheckStyle.Checked;
+				miIsMouseDisabled.Action += () => {
+					miIsMouseDisabled.Checked = Application.IsMouseDisabled = !miIsMouseDisabled.Checked;
+				};
+				menuItems.Add (miIsMouseDisabled);
+
+				return menuItems.ToArray ();
 			}
-		}
 
-		static ColorScheme _colorScheme;
-		static MenuItem [] CreateColorSchemeMenuItems ()
-		{
-			List<MenuItem> menuItems = new List<MenuItem> ();
-			foreach (var sc in Colors.ColorSchemes) {
+			MenuItem [] CreateKeybindingsMenuItems ()
+			{
+				List<MenuItem> menuItems = new List<MenuItem> ();
 				var item = new MenuItem ();
-				item.Title = $"_{sc.Key}";
-				item.Shortcut = Key.AltMask | (Key)sc.Key.Substring (0, 1) [0];
-				item.CheckType |= MenuItemCheckStyle.Radio;
-				item.Checked = sc.Value == _colorScheme;
+				item.Title = "_Key Bindings";
+				item.Help = "Change which keys do what";
 				item.Action += () => {
-					Application.Top.ColorScheme = _colorScheme = sc.Value;
-					Application.Top?.SetNeedsDisplay ();
-					foreach (var menuItem in menuItems) {
-						menuItem.Checked = menuItem.Title.Equals ($"_{sc.Key}") && sc.Value == _colorScheme;
-					}
+					var dlg = new KeyBindingsDialog ();
+					Application.Run (dlg);
 				};
+
+				menuItems.Add (null);
 				menuItems.Add (item);
+
+				return menuItems.ToArray ();
 			}
-			return menuItems.ToArray ();
-		}
 
-		private static void KeyDownHandler (View.KeyEventEventArgs a)
-		{
-			if (a.KeyEvent.IsCapslock) {
-				_capslock.Title = "Caps: On";
-				_statusBar.SetNeedsDisplay ();
-			} else {
-				_capslock.Title = "Caps: Off";
-				_statusBar.SetNeedsDisplay ();
+			MenuItem [] CreateHeightAsBufferMenuItems ()
+			{
+				List<MenuItem> menuItems = new List<MenuItem> ();
+				miHeightAsBuffer = new MenuItem ();
+				miHeightAsBuffer.Title = "_Height As Buffer";
+				miHeightAsBuffer.Shortcut = Key.CtrlMask | Key.AltMask | (Key)miHeightAsBuffer.Title.ToString ().Substring (1, 1) [0];
+				miHeightAsBuffer.CheckType |= MenuItemCheckStyle.Checked;
+				miHeightAsBuffer.Action += () => {
+					miHeightAsBuffer.Checked = !miHeightAsBuffer.Checked;
+					Application.HeightAsBuffer = miHeightAsBuffer.Checked;
+				};
+				menuItems.Add (miHeightAsBuffer);
+
+				return menuItems.ToArray ();
+			}
+
+			MenuItem [] CreateDiagnosticFlagsMenuItems ()
+			{
+				const string OFF = "Diagnostics: _Off";
+				const string FRAME_RULER = "Diagnostics: Frame _Ruler";
+				const string FRAME_PADDING = "Diagnostics: _Frame Padding";
+				var index = 0;
+
+				List<MenuItem> menuItems = new List<MenuItem> ();
+				foreach (Enum diag in Enum.GetValues (_diagnosticFlags.GetType ())) {
+					var item = new MenuItem ();
+					item.Title = GetDiagnosticsTitle (diag);
+					item.Shortcut = Key.AltMask + index.ToString () [0];
+					index++;
+					item.CheckType |= MenuItemCheckStyle.Checked;
+					if (GetDiagnosticsTitle (ConsoleDriver.DiagnosticFlags.Off) == item.Title) {
+						item.Checked = (_diagnosticFlags & (ConsoleDriver.DiagnosticFlags.FramePadding
+						| ConsoleDriver.DiagnosticFlags.FrameRuler)) == 0;
+					} else {
+						item.Checked = _diagnosticFlags.HasFlag (diag);
+					}
+					item.Action += () => {
+						var t = GetDiagnosticsTitle (ConsoleDriver.DiagnosticFlags.Off);
+						if (item.Title == t && !item.Checked) {
+							_diagnosticFlags &= ~(ConsoleDriver.DiagnosticFlags.FramePadding | ConsoleDriver.DiagnosticFlags.FrameRuler);
+							item.Checked = true;
+						} else if (item.Title == t && item.Checked) {
+							_diagnosticFlags |= (ConsoleDriver.DiagnosticFlags.FramePadding | ConsoleDriver.DiagnosticFlags.FrameRuler);
+							item.Checked = false;
+						} else {
+							var f = GetDiagnosticsEnumValue (item.Title);
+							if (_diagnosticFlags.HasFlag (f)) {
+								SetDiagnosticsFlag (f, false);
+							} else {
+								SetDiagnosticsFlag (f, true);
+							}
+						}
+						foreach (var menuItem in menuItems) {
+							if (menuItem.Title == t) {
+								menuItem.Checked = !_diagnosticFlags.HasFlag (ConsoleDriver.DiagnosticFlags.FrameRuler)
+									&& !_diagnosticFlags.HasFlag (ConsoleDriver.DiagnosticFlags.FramePadding);
+							} else if (menuItem.Title != t) {
+								menuItem.Checked = _diagnosticFlags.HasFlag (GetDiagnosticsEnumValue (menuItem.Title));
+							}
+						}
+						ConsoleDriver.Diagnostics = _diagnosticFlags;
+						Application.Top.SetNeedsDisplay ();
+					};
+					menuItems.Add (item);
+				}
+				return menuItems.ToArray ();
+
+				string GetDiagnosticsTitle (Enum diag)
+				{
+					switch (Enum.GetName (_diagnosticFlags.GetType (), diag)) {
+					case "Off":
+						return OFF;
+					case "FrameRuler":
+						return FRAME_RULER;
+					case "FramePadding":
+						return FRAME_PADDING;
+					}
+					return "";
+				}
+
+				Enum GetDiagnosticsEnumValue (ustring title)
+				{
+					switch (title.ToString ()) {
+					case FRAME_RULER:
+						return ConsoleDriver.DiagnosticFlags.FrameRuler;
+					case FRAME_PADDING:
+						return ConsoleDriver.DiagnosticFlags.FramePadding;
+					}
+					return null;
+				}
+
+				void SetDiagnosticsFlag (Enum diag, bool add)
+				{
+					switch (diag) {
+					case ConsoleDriver.DiagnosticFlags.FrameRuler:
+						if (add) {
+							_diagnosticFlags |= ConsoleDriver.DiagnosticFlags.FrameRuler;
+						} else {
+							_diagnosticFlags &= ~ConsoleDriver.DiagnosticFlags.FrameRuler;
+						}
+						break;
+					case ConsoleDriver.DiagnosticFlags.FramePadding:
+						if (add) {
+							_diagnosticFlags |= ConsoleDriver.DiagnosticFlags.FramePadding;
+						} else {
+							_diagnosticFlags &= ~ConsoleDriver.DiagnosticFlags.FramePadding;
+						}
+						break;
+					default:
+						_diagnosticFlags = default;
+						break;
+					}
+				}
 			}
 
-			if (a.KeyEvent.IsNumlock) {
-				_numlock.Title = "Num: On";
-				_statusBar.SetNeedsDisplay ();
-			} else {
-				_numlock.Title = "Num: Off";
-				_statusBar.SetNeedsDisplay ();
+			MenuItem [] CreateColorSchemeMenuItems ()
+			{
+				List<MenuItem> menuItems = new List<MenuItem> ();
+				foreach (var sc in Colors.ColorSchemes) {
+					var item = new MenuItem ();
+					item.Title = $"_{sc.Key}";
+					item.Shortcut = Key.AltMask | (Key)sc.Key.Substring (0, 1) [0];
+					item.CheckType |= MenuItemCheckStyle.Radio;
+					item.Checked = sc.Value == _colorScheme;
+					item.Action += () => {
+						ColorScheme = _colorScheme = sc.Value;
+						SetNeedsDisplay ();
+						foreach (var menuItem in menuItems) {
+							menuItem.Checked = menuItem.Title.Equals ($"_{sc.Key}") && sc.Value == _colorScheme;
+						}
+					};
+					menuItems.Add (item);
+				}
+				return menuItems.ToArray ();
 			}
 
-			if (a.KeyEvent.IsScrolllock) {
-				_scrolllock.Title = "Scroll: On";
-				_statusBar.SetNeedsDisplay ();
-			} else {
-				_scrolllock.Title = "Scroll: Off";
-				_statusBar.SetNeedsDisplay ();
+			void KeyDownHandler (View.KeyEventEventArgs a)
+			{
+				if (a.KeyEvent.IsCapslock) {
+					Capslock.Title = "Caps: On";
+					StatusBar.SetNeedsDisplay ();
+				} else {
+					Capslock.Title = "Caps: Off";
+					StatusBar.SetNeedsDisplay ();
+				}
+
+				if (a.KeyEvent.IsNumlock) {
+					Numlock.Title = "Num: On";
+					StatusBar.SetNeedsDisplay ();
+				} else {
+					Numlock.Title = "Num: Off";
+					StatusBar.SetNeedsDisplay ();
+				}
+
+				if (a.KeyEvent.IsScrolllock) {
+					Scrolllock.Title = "Scroll: On";
+					StatusBar.SetNeedsDisplay ();
+				} else {
+					Scrolllock.Title = "Scroll: Off";
+					StatusBar.SetNeedsDisplay ();
+				}
+			}
+
+			void CategoryListView_SelectedChanged (ListViewItemEventArgs e)
+			{
+				var item = _categories [e.Item];
+				List<Scenario> newlist;
+				if (e.Item == 0) {
+					// First category is "All"
+					newlist = _scenarios;
+
+				} else {
+					newlist = _scenarios.Where (s => s.GetCategories ().Contains (item)).ToList ();
+				}
+				ScenarioListView.SetSource (newlist.ToList ());
 			}
 		}
 
-		private static void CategoryListView_SelectedChanged (ListViewItemEventArgs e)
+		static void VerifyObjectsWereDisposed ()
 		{
-			var item = _categories [e.Item];
-			List<Scenario> newlist;
-			if (e.Item == 0) {
-				// First category is "All"
-				newlist = _scenarios;
-
-			} else {
-				newlist = _scenarios.Where (s => s.GetCategories ().Contains (item)).ToList ();
+#if DEBUG_IDISPOSABLE
+			// Validate there are no outstanding Responder-based instances 
+			// after a scenario was selected to run. This proves the main UI Catalog
+			// 'app' closed cleanly.
+			foreach (var inst in Responder.Instances) {
+				Debug.Assert (inst.WasDisposed);
 			}
-			_scenarioListView.SetSource (newlist.ToList ());
+			Responder.Instances.Clear ();
+
+			// Validate there are no outstanding Application.RunState-based instances 
+			// after a scenario was selected to run. This proves the main UI Catalog
+			// 'app' closed cleanly.
+			foreach (var inst in Application.RunState.Instances) {
+				Debug.Assert (inst.WasDisposed);
+			}
+			Application.RunState.Instances.Clear ();
+#endif
 		}
 
-		private static void OpenUrl (string url)
+		static void OpenUrl (string url)
 		{
 			try {
 				if (RuntimeInformation.IsOSPlatform (OSPlatform.Windows)) {

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 261 - 910
UnitTests/ApplicationTests.cs


+ 3 - 3
UnitTests/MainLoopTests.cs

@@ -578,9 +578,9 @@ namespace Terminal.Gui.Core {
 			TextField tf = new ();
 			Application.Top.Add (tf);
 
-			const int numPasses = 10;
-			const int numIncrements = 10000;
-			const int pollMs = 20000;
+			const int numPasses = 5;
+			const int numIncrements = 5000;
+			const int pollMs = 10000;
 
 			var task = Task.Run (() => RunTest (r, tf, numPasses, numIncrements, pollMs));
 

+ 695 - 0
UnitTests/MdiTests.cs

@@ -0,0 +1,695 @@
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Xunit;
+
+// Alias Console to MockConsole so we don't accidentally use Console
+using Console = Terminal.Gui.FakeConsole;
+
+namespace Terminal.Gui.Core {
+	public class MdiTests {
+		public MdiTests ()
+		{
+#if DEBUG_IDISPOSABLE
+			Responder.Instances.Clear ();
+			Application.RunState.Instances.Clear ();
+#endif
+		}
+
+
+		[Fact]
+		public void Dispose_Toplevel_IsMdiContainer_False_With_Begin_End ()
+		{
+			Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
+
+			var top = new Toplevel ();
+			var rs = Application.Begin (top);
+			Application.End (rs);
+
+			Application.Shutdown ();
+
+			Assert.Equal (2, Responder.Instances.Count);
+			Assert.True (Responder.Instances [0].WasDisposed);
+			Assert.True (Responder.Instances [1].WasDisposed);
+		}
+
+		[Fact]
+		public void Dispose_Toplevel_IsMdiContainer_True_With_Begin ()
+		{
+			Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
+
+			var mdi = new Toplevel { IsMdiContainer = true };
+			var rs = Application.Begin (mdi);
+			Application.End (rs);
+
+			Application.Shutdown ();
+
+			Assert.Equal (2, Responder.Instances.Count);
+			Assert.True (Responder.Instances [0].WasDisposed);
+			Assert.True (Responder.Instances [1].WasDisposed);
+		}
+
+		[Fact, AutoInitShutdown]
+		public void Application_RequestStop_With_Params_On_A_Not_MdiContainer_Always_Use_Application_Current ()
+		{
+			var top1 = new Toplevel ();
+			var top2 = new Toplevel ();
+			var top3 = new Window ();
+			var top4 = new Window ();
+			var d = new Dialog ();
+
+			// top1, top2, top3, d1 = 4
+			var iterations = 4;
+
+			top1.Ready += () => {
+				Assert.Null (Application.MdiChildes);
+				Application.Run (top2);
+			};
+			top2.Ready += () => {
+				Assert.Null (Application.MdiChildes);
+				Application.Run (top3);
+			};
+			top3.Ready += () => {
+				Assert.Null (Application.MdiChildes);
+				Application.Run (top4);
+			};
+			top4.Ready += () => {
+				Assert.Null (Application.MdiChildes);
+				Application.Run (d);
+			};
+
+			d.Ready += () => {
+				Assert.Null (Application.MdiChildes);
+				// This will close the d because on a not MdiContainer the Application.Current it always used.
+				Application.RequestStop (top1);
+				Assert.True (Application.Current == d);
+			};
+
+			d.Closed += (e) => Application.RequestStop (top1);
+
+			Application.Iteration += () => {
+				Assert.Null (Application.MdiChildes);
+				if (iterations == 4) {
+					Assert.True (Application.Current == d);
+				} else if (iterations == 3) {
+					Assert.True (Application.Current == top4);
+				} else if (iterations == 2) {
+					Assert.True (Application.Current == top3);
+				} else if (iterations == 1) {
+					Assert.True (Application.Current == top2);
+				} else {
+					Assert.True (Application.Current == top1);
+				}
+				Application.RequestStop (top1);
+				iterations--;
+			};
+
+			Application.Run (top1);
+
+			Assert.Null (Application.MdiChildes);
+		}
+
+		class Mdi : Toplevel {
+			public Mdi ()
+			{
+				IsMdiContainer = true;
+			}
+		}
+
+		[Fact]
+		[AutoInitShutdown]
+		public void MdiContainer_With_Toplevel_RequestStop_Balanced ()
+		{
+			var mdi = new Mdi ();
+			var c1 = new Toplevel ();
+			var c2 = new Window ();
+			var c3 = new Window ();
+			var d = new Dialog ();
+
+			// MdiChild = c1, c2, c3
+			// d1 = 1
+			var iterations = 4;
+
+			mdi.Ready += () => {
+				Assert.Empty (Application.MdiChildes);
+				Application.Run (c1);
+			};
+			c1.Ready += () => {
+				Assert.Single (Application.MdiChildes);
+				Application.Run (c2);
+			};
+			c2.Ready += () => {
+				Assert.Equal (2, Application.MdiChildes.Count);
+				Application.Run (c3);
+			};
+			c3.Ready += () => {
+				Assert.Equal (3, Application.MdiChildes.Count);
+				Application.Run (d);
+			};
+
+			// More easy because the Mdi Container handles all at once
+			d.Ready += () => {
+				Assert.Equal (3, Application.MdiChildes.Count);
+				// This will not close the MdiContainer because d is a modal toplevel and will be closed.
+				mdi.RequestStop ();
+			};
+
+			// Now this will close the MdiContainer propagating through the MdiChildes.
+			d.Closed += (e) => {
+				mdi.RequestStop ();
+			};
+
+			Application.Iteration += () => {
+				if (iterations == 4) {
+					// The Dialog was not closed before and will be closed now.
+					Assert.True (Application.Current == d);
+					Assert.False (d.Running);
+				} else {
+					Assert.Equal (iterations, Application.MdiChildes.Count);
+					for (int i = 0; i < iterations; i++) {
+						Assert.Equal ((iterations - i + 1).ToString (), Application.MdiChildes [i].Id);
+					}
+				}
+				iterations--;
+			};
+
+			Application.Run (mdi);
+
+			Assert.Empty (Application.MdiChildes);
+		}
+
+		[Fact]
+		[AutoInitShutdown]
+		public void MdiContainer_With_Application_RequestStop_MdiTop_With_Params ()
+		{
+			var mdi = new Mdi ();
+			var c1 = new Toplevel ();
+			var c2 = new Window ();
+			var c3 = new Window ();
+			var d = new Dialog ();
+
+			// MdiChild = c1, c2, c3
+			// d1 = 1
+			var iterations = 4;
+
+			mdi.Ready += () => {
+				Assert.Empty (Application.MdiChildes);
+				Application.Run (c1);
+			};
+			c1.Ready += () => {
+				Assert.Single (Application.MdiChildes);
+				Application.Run (c2);
+			};
+			c2.Ready += () => {
+				Assert.Equal (2, Application.MdiChildes.Count);
+				Application.Run (c3);
+			};
+			c3.Ready += () => {
+				Assert.Equal (3, Application.MdiChildes.Count);
+				Application.Run (d);
+			};
+
+			// Also easy because the Mdi Container handles all at once
+			d.Ready += () => {
+				Assert.Equal (3, Application.MdiChildes.Count);
+				// This will not close the MdiContainer because d is a modal toplevel
+				Application.RequestStop (mdi);
+			};
+
+			// Now this will close the MdiContainer propagating through the MdiChildes.
+			d.Closed += (e) => Application.RequestStop (mdi);
+
+			Application.Iteration += () => {
+				if (iterations == 4) {
+					// The Dialog was not closed before and will be closed now.
+					Assert.True (Application.Current == d);
+					Assert.False (d.Running);
+				} else {
+					Assert.Equal (iterations, Application.MdiChildes.Count);
+					for (int i = 0; i < iterations; i++) {
+						Assert.Equal ((iterations - i + 1).ToString (), Application.MdiChildes [i].Id);
+					}
+				}
+				iterations--;
+			};
+
+			Application.Run (mdi);
+
+			Assert.Empty (Application.MdiChildes);
+		}
+
+		[Fact]
+		[AutoInitShutdown]
+		public void MdiContainer_With_Application_RequestStop_MdiTop_Without_Params ()
+		{
+			var mdi = new Mdi ();
+			var c1 = new Toplevel ();
+			var c2 = new Window ();
+			var c3 = new Window ();
+			var d = new Dialog ();
+
+			// MdiChild = c1, c2, c3 = 3
+			// d1 = 1
+			var iterations = 4;
+
+			mdi.Ready += () => {
+				Assert.Empty (Application.MdiChildes);
+				Application.Run (c1);
+			};
+			c1.Ready += () => {
+				Assert.Single (Application.MdiChildes);
+				Application.Run (c2);
+			};
+			c2.Ready += () => {
+				Assert.Equal (2, Application.MdiChildes.Count);
+				Application.Run (c3);
+			};
+			c3.Ready += () => {
+				Assert.Equal (3, Application.MdiChildes.Count);
+				Application.Run (d);
+			};
+
+			//More harder because it's sequential.
+			d.Ready += () => {
+				Assert.Equal (3, Application.MdiChildes.Count);
+				// Close the Dialog
+				Application.RequestStop ();
+			};
+
+			// Now this will close the MdiContainer propagating through the MdiChildes.
+			d.Closed += (e) => Application.RequestStop (mdi);
+
+			Application.Iteration += () => {
+				if (iterations == 4) {
+					// The Dialog still is the current top and we can't request stop to MdiContainer
+					// because we are not using parameter calls.
+					Assert.True (Application.Current == d);
+					Assert.False (d.Running);
+				} else {
+					Assert.Equal (iterations, Application.MdiChildes.Count);
+					for (int i = 0; i < iterations; i++) {
+						Assert.Equal ((iterations - i + 1).ToString (), Application.MdiChildes [i].Id);
+					}
+				}
+				iterations--;
+			};
+
+			Application.Run (mdi);
+
+			Assert.Empty (Application.MdiChildes);
+		}
+
+		[Fact]
+		[AutoInitShutdown]
+		public void IsMdiChild_Testing ()
+		{
+			var mdi = new Mdi ();
+			var c1 = new Toplevel ();
+			var c2 = new Window ();
+			var c3 = new Window ();
+			var d = new Dialog ();
+
+			Application.Iteration += () => {
+				Assert.False (mdi.IsMdiChild);
+				Assert.True (c1.IsMdiChild);
+				Assert.True (c2.IsMdiChild);
+				Assert.True (c3.IsMdiChild);
+				Assert.False (d.IsMdiChild);
+
+				mdi.RequestStop ();
+			};
+
+			Application.Run (mdi);
+		}
+
+		[Fact]
+		[AutoInitShutdown]
+		public void Modal_Toplevel_Can_Open_Another_Modal_Toplevel_But_RequestStop_To_The_Caller_Also_Sets_Current_Running_To_False_Too ()
+		{
+			var mdi = new Mdi ();
+			var c1 = new Toplevel ();
+			var c2 = new Window ();
+			var c3 = new Window ();
+			var d1 = new Dialog ();
+			var d2 = new Dialog ();
+
+			// MdiChild = c1, c2, c3 = 3
+			// d1, d2 = 2
+			var iterations = 5;
+
+			mdi.Ready += () => {
+				Assert.Empty (Application.MdiChildes);
+				Application.Run (c1);
+			};
+			c1.Ready += () => {
+				Assert.Single (Application.MdiChildes);
+				Application.Run (c2);
+			};
+			c2.Ready += () => {
+				Assert.Equal (2, Application.MdiChildes.Count);
+				Application.Run (c3);
+			};
+			c3.Ready += () => {
+				Assert.Equal (3, Application.MdiChildes.Count);
+				Application.Run (d1);
+			};
+			d1.Ready += () => {
+				Assert.Equal (3, Application.MdiChildes.Count);
+				Application.Run (d2);
+			};
+
+			d2.Ready += () => {
+				Assert.Equal (3, Application.MdiChildes.Count);
+				Assert.True (Application.Current == d2);
+				Assert.True (Application.Current.Running);
+				// Trying to close the Dialog1
+				d1.RequestStop ();
+			};
+
+			// Now this will close the MdiContainer propagating through the MdiChildes.
+			d1.Closed += (e) => {
+				Assert.True (Application.Current == d1);
+				Assert.False (Application.Current.Running);
+				mdi.RequestStop ();
+			};
+
+			Application.Iteration += () => {
+				if (iterations == 5) {
+					// The Dialog2 still is the current top and we can't request stop to MdiContainer
+					// because Dialog2 and Dialog1 must be closed first.
+					// Dialog2 will be closed in this iteration.
+					Assert.True (Application.Current == d2);
+					Assert.False (Application.Current.Running);
+					Assert.False (d1.Running);
+				} else if (iterations == 4) {
+					// Dialog1 will be closed in this iteration.
+					Assert.True (Application.Current == d1);
+					Assert.False (Application.Current.Running);
+				} else {
+					Assert.Equal (iterations, Application.MdiChildes.Count);
+					for (int i = 0; i < iterations; i++) {
+						Assert.Equal ((iterations - i + 1).ToString (), Application.MdiChildes [i].Id);
+					}
+				}
+				iterations--;
+			};
+
+			Application.Run (mdi);
+
+			Assert.Empty (Application.MdiChildes);
+		}
+
+		[Fact]
+		[AutoInitShutdown]
+		public void Modal_Toplevel_Can_Open_Another_Not_Modal_Toplevel_But_RequestStop_To_The_Caller_Also_Sets_Current_Running_To_False_Too ()
+		{
+			var mdi = new Mdi ();
+			var c1 = new Toplevel ();
+			var c2 = new Window ();
+			var c3 = new Window ();
+			var d1 = new Dialog ();
+			var c4 = new Toplevel ();
+
+			// MdiChild = c1, c2, c3, c4 = 4
+			// d1 = 1
+			var iterations = 5;
+
+			mdi.Ready += () => {
+				Assert.Empty (Application.MdiChildes);
+				Application.Run (c1);
+			};
+			c1.Ready += () => {
+				Assert.Single (Application.MdiChildes);
+				Application.Run (c2);
+			};
+			c2.Ready += () => {
+				Assert.Equal (2, Application.MdiChildes.Count);
+				Application.Run (c3);
+			};
+			c3.Ready += () => {
+				Assert.Equal (3, Application.MdiChildes.Count);
+				Application.Run (d1);
+			};
+			d1.Ready += () => {
+				Assert.Equal (3, Application.MdiChildes.Count);
+				Application.Run (c4);
+			};
+
+			c4.Ready += () => {
+				Assert.Equal (4, Application.MdiChildes.Count);
+				// Trying to close the Dialog1
+				d1.RequestStop ();
+			};
+
+			// Now this will close the MdiContainer propagating through the MdiChildes.
+			d1.Closed += (e) => {
+				mdi.RequestStop ();
+			};
+
+			Application.Iteration += () => {
+				if (iterations == 5) {
+					// The Dialog2 still is the current top and we can't request stop to MdiContainer
+					// because Dialog2 and Dialog1 must be closed first.
+					// Using request stop here will call the Dialog again without need
+					Assert.True (Application.Current == d1);
+					Assert.False (Application.Current.Running);
+					Assert.True (c4.Running);
+				} else {
+					Assert.Equal (iterations, Application.MdiChildes.Count);
+					for (int i = 0; i < iterations; i++) {
+						Assert.Equal ((iterations - i + (iterations == 4 && i == 0 ? 2 : 1)).ToString (),
+							Application.MdiChildes [i].Id);
+					}
+				}
+				iterations--;
+			};
+
+			Application.Run (mdi);
+
+			Assert.Empty (Application.MdiChildes);
+		}
+
+		[Fact]
+		[AutoInitShutdown]
+		public void MoveCurrent_Returns_False_If_The_Current_And_Top_Parameter_Are_Both_With_Running_Set_To_False ()
+		{
+			var mdi = new Mdi ();
+			var c1 = new Toplevel ();
+			var c2 = new Window ();
+			var c3 = new Window ();
+
+			// MdiChild = c1, c2, c3
+			var iterations = 3;
+
+			mdi.Ready += () => {
+				Assert.Empty (Application.MdiChildes);
+				Application.Run (c1);
+			};
+			c1.Ready += () => {
+				Assert.Single (Application.MdiChildes);
+				Application.Run (c2);
+			};
+			c2.Ready += () => {
+				Assert.Equal (2, Application.MdiChildes.Count);
+				Application.Run (c3);
+			};
+			c3.Ready += () => {
+				Assert.Equal (3, Application.MdiChildes.Count);
+				c3.RequestStop ();
+				c1.RequestStop ();
+			};
+			// Now this will close the MdiContainer propagating through the MdiChildes.
+			c1.Closed += (e) => {
+				mdi.RequestStop ();
+			};
+			Application.Iteration += () => {
+				if (iterations == 3) {
+					// The Current still is c3 because Current.Running is false.
+					Assert.True (Application.Current == c3);
+					Assert.False (Application.Current.Running);
+					// But the childes order were reorder by Running = false
+					Assert.True (Application.MdiChildes [0] == c3);
+					Assert.True (Application.MdiChildes [1] == c1);
+					Assert.True (Application.MdiChildes [^1] == c2);
+				} else if (iterations == 2) {
+					// The Current is c1 and Current.Running is false.
+					Assert.True (Application.Current == c1);
+					Assert.False (Application.Current.Running);
+					Assert.True (Application.MdiChildes [0] == c1);
+					Assert.True (Application.MdiChildes [^1] == c2);
+				} else if (iterations == 1) {
+					// The Current is c2 and Current.Running is false.
+					Assert.True (Application.Current == c2);
+					Assert.False (Application.Current.Running);
+					Assert.True (Application.MdiChildes [^1] == c2);
+				} else {
+					// The Current is mdi.
+					Assert.True (Application.Current == mdi);
+					Assert.Empty (Application.MdiChildes);
+				}
+				iterations--;
+			};
+
+			Application.Run (mdi);
+
+			Assert.Empty (Application.MdiChildes);
+		}
+
+		[Fact]
+		[AutoInitShutdown]
+		public void MdiContainer_Throws_If_More_Than_One ()
+		{
+			var mdi = new Mdi ();
+			var mdi2 = new Mdi ();
+
+			mdi.Ready += () => {
+				Assert.Throws<InvalidOperationException> (() => Application.Run (mdi2));
+				mdi.RequestStop ();
+			};
+
+			Application.Run (mdi);
+		}
+
+		[Fact]
+		[AutoInitShutdown]
+		public void MdiContainer_Open_And_Close_Modal_And_Open_Not_Modal_Toplevels_Randomly ()
+		{
+			var mdi = new Mdi ();
+			var logger = new Toplevel ();
+
+			var iterations = 1; // The logger
+			var running = true;
+			var stageCompleted = true;
+			var allStageClosed = false;
+			var mdiRequestStop = false;
+
+			mdi.Ready += () => {
+				Assert.Empty (Application.MdiChildes);
+				Application.Run (logger);
+			};
+
+			logger.Ready += () => Assert.Single (Application.MdiChildes);
+
+			Application.Iteration += () => {
+				if (stageCompleted && running) {
+					stageCompleted = false;
+					var stage = new Window () { Modal = true };
+
+					stage.Ready += () => {
+						Assert.Equal (iterations, Application.MdiChildes.Count);
+						stage.RequestStop ();
+					};
+
+					stage.Closed += (_) => {
+						if (iterations == 11) {
+							allStageClosed = true;
+						}
+						Assert.Equal (iterations, Application.MdiChildes.Count);
+						if (running) {
+							stageCompleted = true;
+
+							var rpt = new Window ();
+
+							rpt.Ready += () => {
+								iterations++;
+								Assert.Equal (iterations, Application.MdiChildes.Count);
+							};
+
+							Application.Run (rpt);
+						}
+					};
+
+					Application.Run (stage);
+
+				} else if (iterations == 11 && running) {
+					running = false;
+					Assert.Equal (iterations, Application.MdiChildes.Count);
+
+				} else if (!mdiRequestStop && running && !allStageClosed) {
+					Assert.Equal (iterations, Application.MdiChildes.Count);
+
+				} else if (!mdiRequestStop && !running && allStageClosed) {
+					Assert.Equal (iterations, Application.MdiChildes.Count);
+					mdiRequestStop = true;
+					mdi.RequestStop ();
+				} else {
+					Assert.Empty (Application.MdiChildes);
+				}
+			};
+
+			Application.Run (mdi);
+
+			Assert.Empty (Application.MdiChildes);
+		}
+
+		[Fact]
+		[AutoInitShutdown]
+		public void AllChildClosed_Event_Test ()
+		{
+			var mdi = new Mdi ();
+			var c1 = new Toplevel ();
+			var c2 = new Window ();
+			var c3 = new Window ();
+
+			// MdiChild = c1, c2, c3
+			var iterations = 3;
+
+			mdi.Ready += () => {
+				Assert.Empty (Application.MdiChildes);
+				Application.Run (c1);
+			};
+			c1.Ready += () => {
+				Assert.Single (Application.MdiChildes);
+				Application.Run (c2);
+			};
+			c2.Ready += () => {
+				Assert.Equal (2, Application.MdiChildes.Count);
+				Application.Run (c3);
+			};
+			c3.Ready += () => {
+				Assert.Equal (3, Application.MdiChildes.Count);
+				c3.RequestStop ();
+				c2.RequestStop ();
+				c1.RequestStop ();
+			};
+			// Now this will close the MdiContainer when all MdiChildes was closed
+			mdi.AllChildClosed += () => {
+				mdi.RequestStop ();
+			};
+			Application.Iteration += () => {
+				if (iterations == 3) {
+					// The Current still is c3 because Current.Running is false.
+					Assert.True (Application.Current == c3);
+					Assert.False (Application.Current.Running);
+					// But the childes order were reorder by Running = false
+					Assert.True (Application.MdiChildes [0] == c3);
+					Assert.True (Application.MdiChildes [1] == c2);
+					Assert.True (Application.MdiChildes [^1] == c1);
+				} else if (iterations == 2) {
+					// The Current is c2 and Current.Running is false.
+					Assert.True (Application.Current == c2);
+					Assert.False (Application.Current.Running);
+					Assert.True (Application.MdiChildes [0] == c2);
+					Assert.True (Application.MdiChildes [^1] == c1);
+				} else if (iterations == 1) {
+					// The Current is c1 and Current.Running is false.
+					Assert.True (Application.Current == c1);
+					Assert.False (Application.Current.Running);
+					Assert.True (Application.MdiChildes [^1] == c1);
+				} else {
+					// The Current is mdi.
+					Assert.True (Application.Current == mdi);
+					Assert.False (Application.Current.Running);
+					Assert.Empty (Application.MdiChildes);
+				}
+				iterations--;
+			};
+
+			Application.Run (mdi);
+
+			Assert.Empty (Application.MdiChildes);
+		}
+	}
+}

+ 105 - 0
UnitTests/RunStateTests.cs

@@ -0,0 +1,105 @@
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Xunit;
+
+// Alias Console to MockConsole so we don't accidentally use Console
+using Console = Terminal.Gui.FakeConsole;
+
+namespace Terminal.Gui.Core {
+	/// <summary>
+	/// These tests focus on Application.RunState and the various ways it can be changed.
+	/// </summary>
+	public class RunStateTests {
+		public RunStateTests ()
+		{
+#if DEBUG_IDISPOSABLE
+			Responder.Instances.Clear ();
+			Application.RunState.Instances.Clear ();
+#endif
+		}
+
+		[Fact]
+		public void New_Creates_RunState ()
+		{
+			var rs = new Application.RunState (null);
+			Assert.Null (rs.Toplevel);
+			
+			var top = new Toplevel ();
+			rs = new Application.RunState (top);
+			Assert.Equal (top, rs.Toplevel);
+		}
+
+		[Fact]
+		public void Dispose_Cleans_Up_RunState ()
+		{
+			var rs = new Application.RunState (null);
+			Assert.NotNull (rs);
+
+			// Should not throw because Toplevel was null
+			rs.Dispose ();
+			Assert.True (rs.WasDisposed);
+
+			var top = new Toplevel ();
+			rs = new Application.RunState (top);
+			Assert.NotNull (rs);
+
+			// Should throw because Toplevel was not cleaned up
+			Assert.Throws<InvalidOperationException> (() => rs.Dispose ());
+
+			rs.Toplevel.Dispose ();
+			rs.Toplevel = null;
+			rs.Dispose ();
+			Assert.True (rs.WasDisposed);
+			Assert.True (top.WasDisposed);
+		}
+
+		void Init ()
+		{
+			Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
+			Assert.NotNull (Application.Driver);
+			Assert.NotNull (Application.MainLoop);
+			Assert.NotNull (SynchronizationContext.Current);
+		}
+
+		void Shutdown ()
+		{
+			Application.Shutdown ();
+			// Validate there are no outstanding RunState-based instances left
+			foreach (var inst in Application.RunState.Instances) {
+				Assert.True (inst.WasDisposed);
+			}
+		}
+
+		[Fact]
+		public void Begin_End_Cleans_Up_RunState ()
+		{
+			// Setup Mock driver
+			Init ();
+
+			// Test null Toplevel
+			Assert.Throws<ArgumentNullException> (() => Application.Begin (null));
+
+			var top = new Toplevel ();
+			var rs = Application.Begin (top);
+			Assert.NotNull (rs);
+			Assert.Equal (top, Application.Current);
+			Application.End (rs);
+
+			Assert.Null (Application.Current);
+			Assert.NotNull (Application.Top);
+			Assert.NotNull (Application.MainLoop);
+			Assert.NotNull (Application.Driver);
+
+			Shutdown ();
+
+			Assert.True (rs.WasDisposed);
+
+			Assert.Null (Application.Top);
+			Assert.Null (Application.MainLoop);
+			Assert.Null (Application.Driver);
+		}
+	}
+}

+ 9 - 3
UnitTests/ScenarioTests.cs

@@ -64,12 +64,18 @@ namespace UICatalog {
 				Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
 
 				// Close after a short period of time
-				var token = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (200), closeCallback);
+				var token = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (100), closeCallback);
 
-				scenario.Init (Application.Top, Colors.Base);
+				scenario.Init (Colors.Base);
 				scenario.Setup ();
 				scenario.Run ();
 				Application.Shutdown ();
+#if DEBUG_IDISPOSABLE
+				foreach (var inst in Responder.Instances) {
+					Assert.True (inst.WasDisposed);
+				}
+				Responder.Instances.Clear ();
+#endif
 			}
 #if DEBUG_IDISPOSABLE
 			foreach (var inst in Responder.Instances) {
@@ -115,7 +121,7 @@ namespace UICatalog {
 				Assert.Equal (Key.CtrlMask | Key.Q, args.KeyEvent.Key);
 			};
 
-			generic.Init (Application.Top, Colors.Base);
+			generic.Init (Colors.Base);
 			generic.Setup ();
 			// There is no need to call Application.Begin because Init already creates the Application.Top
 			// If Application.RunState is used then the Application.RunLoop must also be used instead Application.Run.

+ 83 - 0
UnitTests/SynchronizatonContextTests.cs

@@ -0,0 +1,83 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using System.Linq;
+using System.Runtime.InteropServices.ComTypes;
+using System.Threading;
+using System.Threading.Tasks;
+using Terminal.Gui;
+using Xunit;
+using Xunit.Sdk;
+
+// Alias Console to MockConsole so we don't accidentally use Console
+using Console = Terminal.Gui.FakeConsole;
+
+namespace Terminal.Gui.Core {
+	public class SyncrhonizationContextTests {
+
+		[Fact, AutoInitShutdown]
+		public void SynchronizationContext_Post ()
+		{
+			var context = SynchronizationContext.Current;
+
+			var success = false;
+			Task.Run (() => {
+				Thread.Sleep (1_000);
+
+				// non blocking
+				context.Post (
+					delegate (object o) {
+						success = true;
+
+						// then tell the application to quit
+						Application.MainLoop.Invoke (() => Application.RequestStop ());
+					}, null);
+				Assert.False (success);
+			});
+
+			// blocks here until the RequestStop is processed at the end of the test
+			Application.Run ();
+			Assert.True (success);
+		}
+
+		[Fact, AutoInitShutdown]
+		public void SynchronizationContext_Send ()
+		{
+			var context = SynchronizationContext.Current;
+
+			var success = false;
+			Task.Run (() => {
+				Thread.Sleep (1_000);
+
+				// blocking
+				context.Send (
+					delegate (object o) {
+						success = true;
+
+						// then tell the application to quit
+						Application.MainLoop.Invoke (() => Application.RequestStop ());
+					}, null);
+				Assert.True (success);
+			});
+
+			// blocks here until the RequestStop is processed at the end of the test
+			Application.Run ();
+			Assert.True (success);
+
+		}
+
+		[Fact, AutoInitShutdown]
+		public void SynchronizationContext_CreateCopy ()
+		{
+			var context = SynchronizationContext.Current;
+			Assert.NotNull (context);
+
+			var contextCopy = context.CreateCopy ();
+			Assert.NotNull (contextCopy);
+
+			Assert.NotEqual (context, contextCopy);
+		}
+
+	}
+}

+ 2 - 0
UnitTests/ToplevelTests.cs

@@ -431,6 +431,7 @@ namespace Terminal.Gui.Core {
 			var top = Application.Top;
 			Assert.Null (Application.MdiTop);
 			top.IsMdiContainer = true;
+			Application.Begin (top);
 			Assert.Equal (Application.Top, Application.MdiTop);
 
 			var isRunning = true;
@@ -469,6 +470,7 @@ namespace Terminal.Gui.Core {
 			Assert.Null (top.MostFocused);
 			Assert.Equal (win1.Subviews [0], win1.Focused);
 			Assert.Equal (tf1W1, win1.MostFocused);
+			Assert.True (win1.IsMdiChild);
 			Assert.Single (Application.MdiChildes);
 			Application.Begin (win2);
 			Assert.Equal (new Rect (0, 0, 40, 25), win2.Frame);

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно