using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; namespace Terminal.Gui.App; /// /// Interface for instances that provide backing functionality to static /// gateway class . /// /// /// /// Implements to support automatic resource cleanup via using statements. /// Call or use a using statement to properly clean up resources. /// /// public interface IApplication : IDisposable { #region Lifecycle - App Initialization and Shutdown /// /// Gets or sets the managed thread ID of the application's main UI thread, which is set during /// and used to determine if code is executing on the main thread. /// /// /// The managed thread ID of the main UI thread, or if the application is not initialized. /// public int? MainThreadId { get; internal set; } /// Initializes a new instance of Application. /// /// The short name (e.g. "dotnet", "windows", "unix", or "fake") of the /// to use. If not specified the default driver for the platform will be used. /// /// This instance for fluent API chaining. /// /// Call this method once per instance (or after has been called). /// /// This function loads the right for the platform, creates a main loop coordinator, /// initializes keyboard and mouse handlers, and subscribes to driver events. /// /// /// must be called when the application is closing (typically after /// has returned) to ensure all resources are cleaned up (disposed) and /// terminal settings are restored. /// /// /// Supports fluent API with automatic resource management: /// /// /// Recommended pattern (using statement): /// /// using (var app = Application.Create().Init()) /// { /// app.Run<MyDialog>(); /// var result = app.GetResult<MyResultType>(); /// } // app.Dispose() called automatically /// /// /// /// Alternative pattern (manual disposal): /// /// var app = Application.Create().Init(); /// app.Run<MyDialog>(); /// var result = app.GetResult<MyResultType>(); /// app.Dispose(); // Must call explicitly /// /// /// /// Note: Runnables created by are automatically disposed when /// that method returns. Runnables passed to /// must be disposed by the caller. /// /// [RequiresUnreferencedCode ("AOT")] [RequiresDynamicCode ("AOT")] public IApplication Init (string? driverName = null); /// /// This event is raised after the and methods have been called. /// /// /// Intended to support unit tests that need to know when the application has been initialized. /// public event EventHandler>? InitializedChanged; /// Gets or sets whether the application has been initialized. bool Initialized { get; set; } /// /// INTERNAL: Resets the state of this instance. Called by Dispose. /// /// If true, ignores disposed state checks during reset. /// /// /// Encapsulates all setting of initial state for Application; having this in a function like this ensures we /// don't make mistakes in guaranteeing that the state of this singleton is deterministic when /// starts running and after returns. /// /// /// IMPORTANT: Ensure all property/fields are reset here. See Init_ResetState_Resets_Properties unit test. /// /// internal void ResetState (bool ignoreDisposed = false); #endregion App Initialization and Shutdown #region Session Management - Begin->Run->Iteration->Stop->End /// /// Gets the stack of all active runnable session tokens. /// Sessions execute serially - the top of stack is the currently modal session. /// /// /// /// Session tokens are pushed onto the stack when is called and /// popped when /// completes. The stack grows during nested modal calls and /// shrinks as they complete. /// /// /// Only the top session () has exclusive keyboard/mouse input ( /// = true). /// All other sessions on the stack continue to be laid out, drawn, and receive iteration events ( /// = true), /// but they don't receive user input. /// /// /// Stack during nested modals: /// /// RunnableSessionStack (top to bottom): /// - MessageBox (TopRunnable, IsModal=true, IsRunning=true, has input) /// - FileDialog (IsModal=false, IsRunning=true, continues to update/draw) /// - MainWindow (IsModal=false, IsRunning=true, continues to update/draw) /// /// /// ConcurrentStack? SessionStack { get; } /// /// Raised when has been called and has created a new . /// /// /// If is , callers to /// must also subscribe to and manually dispose of the token /// when the application is done. /// public event EventHandler? SessionBegun; #region TopRunnable Properties /// Gets the Runnable that is on the top of the . /// /// /// The top runnable in the session stack captures all mouse and keyboard input. /// This is set by and cleared by . /// /// IRunnable? TopRunnable { get; } /// Gets the View that is on the top of the . /// /// /// This is a convenience property that casts to a . /// /// View? TopRunnableView { get; } #endregion TopRunnable Properties /// /// Building block API: Creates a and prepares the provided /// for /// execution. Not usually called directly by applications. Use /// instead. /// /// The to prepare execution for. /// /// The that needs to be passed to the /// method upon /// completion. /// /// /// /// This method prepares the provided for running. It adds this to the /// , lays out the SubViews, focuses the first element, and draws the /// runnable on the screen. This is usually followed by starting the main loop, and then the /// method upon termination which will undo these changes. /// /// /// Raises the , , /// and events. /// /// /// The session token. if the operation was cancelled. SessionToken? Begin (IRunnable runnable); /// /// Runs a new Session with the provided runnable view. /// /// The runnable to execute. /// Optional handler for unhandled exceptions (resumes when returns true, rethrows when null). /// /// /// This method is used to start processing events for the main application, but it is also used to run other /// modal views such as dialogs. /// /// /// To make stop execution, call /// or . /// /// /// Calling is equivalent to calling /// , followed by starting the main loop, and then calling /// . /// /// /// In RELEASE builds: When is any exceptions will be /// rethrown. Otherwise, will be called. If /// returns the main loop will resume; otherwise this method will exit. /// /// object? Run (IRunnable runnable, Func? errorHandler = null); /// /// Runs a new Session creating a -derived object of type /// and calling . When the session is stopped, /// will be called. /// /// /// Handler for any unhandled exceptions (resumes when returns true, rethrows when null). /// /// The driver name. If not specified the default driver for the platform will be used. Must be /// if has already been called. /// /// /// The created object. The caller is responsible for calling /// on this /// object. /// /// /// /// This method is used to start processing events for the main application, but it is also used to run other /// modal s such as boxes. /// /// /// To make stop execution, call /// or . /// /// /// In RELEASE builds: When is any exceptions will be /// rethrown. Otherwise, will be called. If /// returns the main loop will resume; otherwise this method will exit. /// /// /// must be called when the application is closing (typically after Run has /// returned) to /// ensure resources are cleaned up and terminal settings restored. /// /// /// In RELEASE builds: When is any exceptions will be /// rethrown. Otherwise, will be called. If /// returns the main loop will resume; otherwise this method will exit. /// /// /// The caller is responsible for disposing the object returned by this method. /// /// [RequiresUnreferencedCode ("AOT")] [RequiresDynamicCode ("AOT")] public IApplication Run (Func? errorHandler = null, string? driverName = null) where TRunnable : IRunnable, new (); #region Iteration & Invoke /// /// Raises the event. /// /// /// This is called once per main loop iteration, before processing input, timeouts, or rendering. /// public void RaiseIteration (); /// This event is raised on each iteration of the main loop. /// /// /// This event is raised before input processing, timeout callbacks, and rendering occur each iteration. /// /// The event args contain the current application instance. /// /// /// /// . public event EventHandler>? Iteration; /// Runs on the main UI loop thread. /// The action to be invoked on the main processing thread. /// /// /// If called from the main thread, the action is executed immediately. Otherwise, it is queued via /// with and will be executed on the next main loop /// iteration. /// /// void Invoke (Action? action); /// Runs on the main UI loop thread. /// The action to be invoked on the main processing thread. /// /// /// If called from the main thread, the action is executed immediately. Otherwise, it is queued via /// with and will be executed on the next main loop /// iteration. /// /// void Invoke (Action action); #endregion Iteration & Invoke /// /// Set to to cause the session to stop running after first iteration. /// /// /// /// Used primarily for unit testing. When , will be /// called /// automatically after the first main loop iteration. /// /// bool StopAfterFirstIteration { get; set; } /// Requests that the currently running Session stop. The Session will stop after the current iteration completes. /// /// This will cause to return. /// /// This is equivalent to calling with as the /// parameter. /// /// void RequestStop (); /// /// Requests that the specified runnable session stop. /// /// /// The runnable to stop. If , stops the current /// . /// /// /// /// This will cause to return. /// /// /// Raises , , /// and events. /// /// void RequestStop (IRunnable? runnable); /// /// Building block API: Ends the session associated with the token and completes the execution of an /// . /// Not usually called directly by applications. /// will automatically call this method when the session is stopped. /// /// /// The returned by the /// method. /// /// /// /// This method removes the from the , /// raises the lifecycle events, and disposes the . /// /// /// Raises , , /// and events. /// /// void End (SessionToken sessionToken); /// /// Raised when was called and the session is stopping. The event args contain a /// reference to the /// that was active during the session. This can be used to ensure the Runnable is disposed of properly. /// /// /// If is , callers to /// must also subscribe to and manually dispose of the token /// when the application is done. /// public event EventHandler? SessionEnded; #endregion Session Management - Begin->Run->Iteration->Stop->End #region Result Management /// /// Gets the result from the last or /// call. /// /// /// The result from the last run session, or if no session has been run or the result was null. /// object? GetResult (); /// /// Gets the result from the last or /// call, cast to type . /// /// The expected result type. /// /// The result cast to , or if the result is null or cannot be cast. /// /// /// /// using (var app = Application.Create().Init()) /// { /// app.Run<ColorPickerDialog>(); /// var selectedColor = app.GetResult<Color>(); /// if (selectedColor.HasValue) /// { /// // Use the color /// } /// } /// /// T? GetResult () where T : class => GetResult () as T; #endregion Result Management #region Screen and Driver /// Gets or sets the console driver being used. /// /// /// Set by based on the driver parameter or platform default. /// /// IDriver? Driver { get; set; } /// /// Gets the clipboard for this application instance. /// /// /// /// Provides access to the OS clipboard through the driver. Returns if /// is not initialized. /// /// IClipboard? Clipboard { get; } /// /// Forces the use of the specified driver (one of "fake", "dotnet", "windows", or "unix"). If not /// specified, the driver is selected based on the platform. /// string ForceDriver { get; set; } /// /// Gets or sets the size of the screen. By default, this is the size of the screen as reported by the /// . /// /// /// /// If the has not been initialized, this will return a default size of 2048x2048; useful /// for unit tests. /// /// Rectangle Screen { get; set; } /// Raised when the terminal's size changed. The new size of the terminal is provided. /// /// /// This event is raised when the driver detects a screen size change. The event provides the new screen /// rectangle. /// /// public event EventHandler>? ScreenChanged; /// /// Gets or sets whether the screen will be cleared, and all Views redrawn, during the next Application iteration. /// /// /// /// This is typically set to when a View's changes and that view /// has no SuperView (e.g. when is moved or resized). /// /// /// Automatically reset to after processes it. /// /// bool ClearScreenNextIteration { get; set; } #endregion Screen and Driver #region Keyboard /// /// Handles keyboard input and key bindings at the Application level. /// /// /// /// Provides access to keyboard state, key bindings, and keyboard event handling. Set during . /// /// IKeyboard Keyboard { get; set; } #endregion Keyboard #region Mouse /// /// Handles mouse event state and processing. /// /// /// /// Provides access to mouse state, mouse grabbing, and mouse event handling. Set during . /// /// IMouse Mouse { get; set; } #endregion Mouse #region Layout and Drawing /// /// Causes any Runnables that need layout to be laid out, then draws any Runnables that need display. Only Views /// that need to be laid out (see ) will be laid out. Only Views that need to be drawn /// (see ) will be drawn. /// /// /// If the entire View hierarchy will be redrawn. The default is and /// should only be overridden for testing. /// /// /// /// This method is called automatically each main loop iteration when any views need layout or drawing. /// /// /// If is , the screen will be cleared before /// drawing and the flag will be reset to . /// /// public void LayoutAndDraw (bool forceRedraw = false); /// /// Calls on the most focused view. /// /// /// Does nothing if there is no most focused view. /// /// If the most focused view is not visible within its superview, the cursor will be hidden. /// /// /// if a view positioned the cursor and the position is visible. public bool PositionCursor (); #endregion Layout and Drawing #region Navigation and Popover /// Gets or sets the navigation manager. /// /// /// Manages focus navigation and tracking of the most focused view. Initialized during . /// /// ApplicationNavigation? Navigation { get; set; } /// Gets or sets the popover manager. /// /// /// Manages application-level popover views. Initialized during . /// /// ApplicationPopover? Popover { get; set; } #endregion Navigation and Popover #region Timeouts /// Adds a timeout to the application. /// The time span to wait before invoking the callback. /// /// The callback to invoke. If it returns , the timeout will be reset and repeat. If it /// returns , the timeout will stop and be removed. /// /// /// Call with the returned value to stop the timeout. /// /// /// /// When the time specified passes, the callback will be invoked on the main UI thread. /// /// /// calls StopAll on to remove all timeouts. /// /// object? AddTimeout (TimeSpan time, Func callback); /// Removes a previously scheduled timeout. /// The token returned by . /// /// if the timeout is successfully removed; otherwise, . /// This method also returns if the timeout is not found. /// bool RemoveTimeout (object token); /// /// Handles recurring events. These are invoked on the main UI thread - allowing for /// safe updates to instances. /// /// /// /// Provides low-level access to the timeout management system. Most applications should use /// and instead. /// /// ITimedEvents? TimedEvents { get; } #endregion Timeouts /// /// Gets a string representation of the Application as rendered by . /// /// A string representation of the Application public string ToString (); }