diff --git a/Terminal.Gui/Core/Application.cs b/Terminal.Gui/Core/Application.cs index 72ce9a4c6..50906cfb9 100644 --- a/Terminal.Gui/Core/Application.cs +++ b/Terminal.Gui/Core/Application.cs @@ -39,6 +39,7 @@ namespace Terminal.Gui { /// }; /// Application.Top.Add(win); /// Application.Run(); + /// Application.Shutdown(); /// /// /// @@ -302,25 +303,44 @@ namespace Terminal.Gui { /// /// Initializes a new instance of Application. /// - /// /// /// Call this method once per instance (or after has been called). /// /// - /// Loads the right for the platform. + /// This function loads the right for the platform, + /// Creates a . and assigns it to /// /// - /// Creates a and assigns it to + /// must be called when the application is closing (typically after has + /// returned) to ensure resources are cleaned up and terminal settings restored. /// + /// + /// The function combines and + /// into a single call. An applciation cam use without explicitly calling . + /// + /// + /// Note, due to Issue #520, if Init is called and is not called, the + /// created by this function will NOT be Disposed when is called. Call Dispose() on + /// before calling if (and ) has not been called. /// + /// The to use. If not specified the default driver for the + /// platform will be used (see , , and ). + /// Specifies the to use. public static void Init (ConsoleDriver driver = null, IMainLoopDriver mainLoopDriver = null) => Init (() => Toplevel.Create (), driver, mainLoopDriver); internal static bool _initialized = false; internal static int _mainThreadId = -1; /// - /// Initializes the Terminal.Gui application + /// Internal function for initializing a Terminal.Gui application with a factory object, + /// a , and . + /// + /// This is a low-level function; most applications will use as it is simpler. /// + /// Specifies the factory funtion./> + /// The to use. If not specified the default driver for the + /// platform will be used (see , , and ). + /// Specifies the to use. static void Init (Func topLevelFactory, ConsoleDriver driver = null, IMainLoopDriver mainLoopDriver = null) { if (_initialized && driver == null) return; @@ -802,8 +822,8 @@ namespace Terminal.Gui { /// /// Building block API: Prepares the provided for execution. /// - /// The runstate handle that needs to be passed to the method upon completion. - /// Toplevel to prepare execution for. + /// The handle that needs to be passed to the method upon completion. + /// The to prepare execution for. /// /// 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 @@ -897,9 +917,9 @@ namespace Terminal.Gui { } /// - /// Building block API: completes the execution of a that was started with . + /// Building block API: completes the execution of a that was started with . /// - /// The runstate returned by the method. + /// The returned by the method. public static void End (RunState runState) { if (runState == null) @@ -914,8 +934,12 @@ namespace Terminal.Gui { } /// - /// Shutdown an application initialized with + /// Shutdown an application initialized with . /// + /// + /// Shutdown must be called for every call to or + /// to ensure all resources are cleaned up (Disposed) and terminal settings are restored. + /// public static void Shutdown () { ResetState (); @@ -1016,7 +1040,7 @@ namespace Terminal.Gui { } /// - /// Building block API: Runs the main loop for the created dialog + /// Building block API: Runs the main loop for the created dialog. /// /// /// Use the wait parameter to control whether this is a @@ -1040,11 +1064,13 @@ namespace Terminal.Gui { } /// - /// Run one iteration of the MainLoop. + /// Run one iteration of the . /// - /// The state returned by the Begin method. - /// If will execute the runloop waiting for events. - /// If it's the first run loop iteration. + /// The state returned by . + /// If will execute the runloop waiting for events. If + /// will return after a single iteration. + /// Set to if this is the first run loop iteration. Upon return, + /// it will be set to if at least one iteration happened. public static void RunMainLoopIteration (ref RunState state, bool wait, ref bool firstIteration) { if (MainLoop.EventsPending (wait)) { @@ -1145,8 +1171,11 @@ namespace Terminal.Gui { } /// - /// Runs the application by calling with the value of + /// Runs the application by calling with the value of . /// + /// + /// See for more details. + /// public static void Run (Func errorHandler = null) { Run (Top, errorHandler); @@ -1159,7 +1188,14 @@ namespace Terminal.Gui { /// If has not arleady been called, this function will /// call . /// + /// + /// must be called when the application is closing (typically after has + /// returned) to ensure resources are cleaned up and terminal settings restored. + /// /// + /// + /// See for more details. + /// public static void Run (Func errorHandler = null) where T : Toplevel, new() { if (_initialized && Driver != null) { @@ -1197,16 +1233,19 @@ namespace Terminal.Gui { /// /// Alternatively, to have a program control the main loop and /// process events manually, call to set things up manually and then - /// repeatedly call with the wait parameter set to false. By doing this + /// repeatedly call with the wait parameter set to false. By doing this /// the method will only process any pending events, timers, idle handlers and /// then return control immediately. /// /// - /// When is null the exception is rethrown, when it returns true the application is resumed and when false method exits gracefully. + /// RELEASE builds only: When is any exeptions will be rethrown. + /// Otheriwse, if will be called. If + /// returns the will resume; otherwise + /// this method will exit. /// /// /// The to run modally. - /// Handler for any unhandled exceptions (resumes when returns true, rethrows when null). + /// RELEASE builds only: Handler for any unhandled exceptions (resumes when returns true, rethrows when null). public static void Run (Toplevel view, Func errorHandler = null) { var resume = true; diff --git a/UICatalog/Scenarios/BackgroundWorkerCollection.cs b/UICatalog/Scenarios/BackgroundWorkerCollection.cs index eaadc66f4..480952347 100644 --- a/UICatalog/Scenarios/BackgroundWorkerCollection.cs +++ b/UICatalog/Scenarios/BackgroundWorkerCollection.cs @@ -14,11 +14,21 @@ namespace UICatalog.Scenarios { public class BackgroundWorkerCollection : Scenario { public override void Init (Toplevel top, ColorScheme colorScheme) { - // Do nothing as the call to `Application.Run` in `Run` implies an `Application.Init()` call. + //Application.Init (); } public override void Run () { + // For Scenarios that want to use `Applciation.Run` to create a new Toplevel, there are two choices: + + // 1) Override `Scenario.Init` and do nothing in it. + // The call to `Application.Run` in `Run` implies an `Application.Init()` call. + // + // 2) Just override `Run` but call `Application.Top.Dispose` then `Application.Shutdown ()`. This + // works around bug #520, ensuring the `Toplevel` created by `Init` gets Disposed. + //Application.Top.Dispose (); + //Application.Shutdown (); + Application.Run (); } diff --git a/UnitTests/ApplicationTests.cs b/UnitTests/ApplicationTests.cs index e0f331e74..597534c74 100644 --- a/UnitTests/ApplicationTests.cs +++ b/UnitTests/ApplicationTests.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -31,13 +32,23 @@ namespace Terminal.Gui.Core { Assert.Equal (80, Application.Driver.Cols); Assert.Equal (25, Application.Driver.Rows); + // Because of #520, the Toplevel created by Application.Init is not disposed by Shutdown + // So we need to dispose it manually + Application.Top.Dispose (); + Application.Shutdown (); // Verify state is back to initial Pre_Init_State (); - + + // 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) { + Assert.True (inst.WasDisposed); + } } - + void Pre_Init_State () { Assert.Null (Application.Driver); @@ -91,9 +102,9 @@ namespace Terminal.Gui.Core { { Application.Shutdown (); } - + [Fact] - public void Begin_End_Cleana_Up () + public void Begin_End_Cleans_Up () { // Setup Mock driver Init ();