From 0f72cf8a744b785fcbb665773a08067cd8a6ffc9 Mon Sep 17 00:00:00 2001 From: Tig Date: Mon, 1 Dec 2025 14:40:31 -0700 Subject: [PATCH] Fixes #4425 - `ApplicationImpl` internal (#4426) * Pulled from v2_release * Refactor migration guide for Terminal.Gui v2 Restructured and expanded the migration guide to provide a comprehensive resource for transitioning from Terminal.Gui v1 to v2. Key updates include: - Added a Table of Contents for easier navigation. - Summarized major architectural changes in v2, including the instance-based application model, IRunnable architecture, and 24-bit TrueColor support. - Updated examples to reflect new patterns, such as initializers replacing constructors and explicit disposal using `IDisposable`. - Documented changes to the layout system, including the removal of `Absolute`/`Computed` styles and the introduction of `Viewport`. - Standardized event patterns to use `object sender, EventArgs args`. - Detailed updates to the Keyboard, Mouse, and Navigation APIs, including configurable key bindings and viewport-relative mouse coordinates. - Replaced legacy components like `ScrollView` and `ContextMenu` with built-in scrolling and `PopoverMenu`. - Clarified disposal rules and introduced best practices for resource management. - Provided a complete migration example and a summary of breaking changes. This update aims to simplify the migration process by addressing breaking changes, introducing new features, and aligning with modern .NET conventions. * Refactor to use Application.Instance for lifecycle management Replaced all occurrences of `ApplicationImpl.Instance` with the new `Application.Instance` property across the codebase to align with the updated application lifecycle model. Encapsulated the `ApplicationImpl` class by making it `internal`, ensuring it is no longer directly accessible outside its assembly. Introduced the `[Obsolete]` `Application.Instance` property as a backward-compatible singleton for the legacy static `Application` model, while encouraging the use of `Application.Create()` for new code. Updated `MessageBox` methods to use `Application.Instance` for consistent modal dialog management. Improved documentation to reflect these changes and emphasize the transition to the instance-based application model. Performed code cleanup in multiple classes to ensure consistency and maintainability. These changes maintain backward compatibility while preparing the codebase for the eventual removal of the legacy `ApplicationImpl` class. * Fix doc bug * - Removed obsolete `.cd` class diagram files. - Introduced `IRunnable` interface for decoupling component execution. - Added fluent API for running dialogs and retrieving results. - Enhanced `View` with `App` and `Driver` properties for better decoupling. - Improved testability with support for mock and real applications. - Implemented `IDisposable` for proper resource cleanup. - Replaced `RunnableSessionStack` with `SessionStack` for session management. - Updated driver architecture to align with the new model. - Scoped `IKeyboard` to application contexts for modularity. - Updated documentation with migration strategies and best practices. These changes modernize the library, improve maintainability, and align with current development practices. --- Examples/UICatalog/Scenarios/ContextMenus.cs | 2 +- Examples/UICatalog/Scenarios/CsvEditor.cs | 26 +- .../UICatalog/Scenarios/DynamicStatusBar.cs | 4 +- Examples/UICatalog/Scenarios/Editor.cs | 18 +- .../UICatalog/Scenarios/FileDialogExamples.cs | 10 +- Examples/UICatalog/Scenarios/Generic.cs | 2 +- Examples/UICatalog/Scenarios/HexEditor.cs | 8 +- Examples/UICatalog/Scenarios/Images.cs | 6 +- .../UICatalog/Scenarios/InteractiveTree.cs | 2 +- Examples/UICatalog/Scenarios/KeyBindings.cs | 8 +- Examples/UICatalog/Scenarios/ListColumns.cs | 2 +- Examples/UICatalog/Scenarios/MessageBoxes.cs | 4 +- .../UICatalog/Scenarios/MultiColouredTable.cs | 2 +- Examples/UICatalog/Scenarios/Navigation.cs | 2 +- Examples/UICatalog/Scenarios/Notepad.cs | 4 +- .../Scenarios/RuneWidthGreaterThanOne.cs | 6 +- .../Scenarios/WindowsAndFrameViews.cs | 2 +- Examples/UICatalog/Scenarios/WizardAsView.cs | 6 +- Terminal.Gui/App/Application.Lifecycle.cs | 18 +- Terminal.Gui/App/Application.cd | 117 ------ Terminal.Gui/App/ApplicationImpl.Driver.cs | 2 +- Terminal.Gui/App/ApplicationImpl.Lifecycle.cs | 2 +- Terminal.Gui/App/ApplicationImpl.Run.cs | 2 +- Terminal.Gui/App/ApplicationImpl.Screen.cs | 2 +- Terminal.Gui/App/ApplicationImpl.cs | 4 +- .../Views/Autocomplete/Autocomplete.cd | 79 ---- .../CollectionNavigation.cd | 124 ------ Terminal.Gui/Views/FileDialogs/FileDialog.cd | 168 -------- Terminal.Gui/Views/MessageBox.cs | 2 +- Terminal.Gui/Views/Shortcut.cs | 2 +- .../TerminalGuiFluentTesting/ClassDiagram1.cd | 89 ----- docfx/docs/application.md | 377 +++++++++--------- docfx/docs/drivers.md | 7 +- docfx/docs/keyboard.md | 2 +- 34 files changed, 278 insertions(+), 833 deletions(-) delete mode 100644 Terminal.Gui/App/Application.cd delete mode 100644 Terminal.Gui/Views/Autocomplete/Autocomplete.cd delete mode 100644 Terminal.Gui/Views/CollectionNavigation/CollectionNavigation.cd delete mode 100644 Terminal.Gui/Views/FileDialogs/FileDialog.cd delete mode 100644 Tests/TerminalGuiFluentTesting/ClassDiagram1.cd diff --git a/Examples/UICatalog/Scenarios/ContextMenus.cs b/Examples/UICatalog/Scenarios/ContextMenus.cs index 4ed0f02c8..029798b0a 100644 --- a/Examples/UICatalog/Scenarios/ContextMenus.cs +++ b/Examples/UICatalog/Scenarios/ContextMenus.cs @@ -49,7 +49,7 @@ public class ContextMenus : Scenario var text = "Context Menu"; var width = 20; - CreateWinContextMenu (ApplicationImpl.Instance); + CreateWinContextMenu (Application.Instance); var label = new Label { diff --git a/Examples/UICatalog/Scenarios/CsvEditor.cs b/Examples/UICatalog/Scenarios/CsvEditor.cs index aad85f6aa..0c676f805 100644 --- a/Examples/UICatalog/Scenarios/CsvEditor.cs +++ b/Examples/UICatalog/Scenarios/CsvEditor.cs @@ -215,7 +215,7 @@ public class CsvEditor : Scenario _tableView.Table.Columns ); - int? result = MessageBox.Query (ApplicationImpl.Instance, + int? result = MessageBox.Query (Application.Instance, "Column Type", "Pick a data type for the column", "Date", @@ -308,7 +308,7 @@ public class CsvEditor : Scenario if (_tableView.SelectedColumn == -1) { - MessageBox.ErrorQuery (ApplicationImpl.Instance, "No Column", "No column selected", "Ok"); + MessageBox.ErrorQuery (Application.Instance, "No Column", "No column selected", "Ok"); return; } @@ -320,7 +320,7 @@ public class CsvEditor : Scenario } catch (Exception ex) { - MessageBox.ErrorQuery (ApplicationImpl.Instance, "Could not remove column", ex.Message, "Ok"); + MessageBox.ErrorQuery (Application.Instance, "Could not remove column", ex.Message, "Ok"); } } @@ -342,7 +342,7 @@ public class CsvEditor : Scenario } catch (Exception ex) { - MessageBox.ErrorQuery (ApplicationImpl.Instance, 60, 20, "Failed to set text", ex.Message, "Ok"); + MessageBox.ErrorQuery (Application.Instance, 60, 20, "Failed to set text", ex.Message, "Ok"); } _tableView.Update (); @@ -388,7 +388,7 @@ public class CsvEditor : Scenario if (_tableView.SelectedColumn == -1) { - MessageBox.ErrorQuery (ApplicationImpl.Instance, "No Column", "No column selected", "Ok"); + MessageBox.ErrorQuery (Application.Instance, "No Column", "No column selected", "Ok"); return; } @@ -413,7 +413,7 @@ public class CsvEditor : Scenario } catch (Exception ex) { - MessageBox.ErrorQuery (ApplicationImpl.Instance, "Error moving column", ex.Message, "Ok"); + MessageBox.ErrorQuery (Application.Instance, "Error moving column", ex.Message, "Ok"); } } @@ -426,7 +426,7 @@ public class CsvEditor : Scenario if (_tableView.SelectedRow == -1) { - MessageBox.ErrorQuery (ApplicationImpl.Instance, "No Rows", "No row selected", "Ok"); + MessageBox.ErrorQuery (Application.Instance, "No Rows", "No row selected", "Ok"); return; } @@ -462,7 +462,7 @@ public class CsvEditor : Scenario } catch (Exception ex) { - MessageBox.ErrorQuery (ApplicationImpl.Instance, "Error moving column", ex.Message, "Ok"); + MessageBox.ErrorQuery (Application.Instance, "Error moving column", ex.Message, "Ok"); } } @@ -470,7 +470,7 @@ public class CsvEditor : Scenario { if (_tableView?.Table is null) { - MessageBox.ErrorQuery (ApplicationImpl.Instance, "No Table Loaded", "No table has currently be opened", "Ok"); + MessageBox.ErrorQuery (Application.Instance, "No Table Loaded", "No table has currently be opened", "Ok"); return true; } @@ -582,7 +582,7 @@ public class CsvEditor : Scenario } catch (Exception ex) { - MessageBox.ErrorQuery (ApplicationImpl.Instance, + MessageBox.ErrorQuery (Application.Instance, "Open Failed", $"Error on line {lineNumber}{Environment.NewLine}{ex.Message}", "Ok" @@ -612,7 +612,7 @@ public class CsvEditor : Scenario { if (_tableView?.Table is null || string.IsNullOrWhiteSpace (_currentFile) || _currentTable is null) { - MessageBox.ErrorQuery (ApplicationImpl.Instance, "No file loaded", "No file is currently loaded", "Ok"); + MessageBox.ErrorQuery (Application.Instance, "No file loaded", "No file is currently loaded", "Ok"); return; } @@ -674,7 +674,7 @@ public class CsvEditor : Scenario if (col.DataType == typeof (string)) { - MessageBox.ErrorQuery (ApplicationImpl.Instance, + MessageBox.ErrorQuery (Application.Instance, "Cannot Format Column", "String columns cannot be Formatted, try adding a new column to the table with a date/numerical Type", "Ok" @@ -711,7 +711,7 @@ public class CsvEditor : Scenario if (_tableView.SelectedColumn == -1) { - MessageBox.ErrorQuery (ApplicationImpl.Instance, "No Column", "No column selected", "Ok"); + MessageBox.ErrorQuery (Application.Instance, "No Column", "No column selected", "Ok"); return; } diff --git a/Examples/UICatalog/Scenarios/DynamicStatusBar.cs b/Examples/UICatalog/Scenarios/DynamicStatusBar.cs index ac558f22a..43b01829f 100644 --- a/Examples/UICatalog/Scenarios/DynamicStatusBar.cs +++ b/Examples/UICatalog/Scenarios/DynamicStatusBar.cs @@ -79,7 +79,7 @@ public class DynamicStatusBar : Scenario } catch (Exception ex) { - MessageBox.ErrorQuery (ApplicationImpl.Instance, "Binding Error", $"Binding failed: {ex}.", "Ok"); + MessageBox.ErrorQuery (Application.Instance, "Binding Error", $"Binding failed: {ex}.", "Ok"); } } } @@ -140,7 +140,7 @@ public class DynamicStatusBar : Scenario public TextView TextAction { get; } public TextField TextShortcut { get; } public TextField TextTitle { get; } - public Action CreateAction (DynamicStatusItem item) { return () => MessageBox.ErrorQuery (ApplicationImpl.Instance, item.Title, item.Action, "Ok"); } + public Action CreateAction (DynamicStatusItem item) { return () => MessageBox.ErrorQuery (Application.Instance, item.Title, item.Action, "Ok"); } public void EditStatusItem (Shortcut statusItem) { diff --git a/Examples/UICatalog/Scenarios/Editor.cs b/Examples/UICatalog/Scenarios/Editor.cs index 857663577..ddf16c92e 100644 --- a/Examples/UICatalog/Scenarios/Editor.cs +++ b/Examples/UICatalog/Scenarios/Editor.cs @@ -201,7 +201,7 @@ public class Editor : Scenario Debug.Assert (_textView.IsDirty); int? r = MessageBox.ErrorQuery ( - ApplicationImpl.Instance, + Application.Instance, "Save File", $"Do you want save changes in {_appWindow.Title}?", "Yes", @@ -236,7 +236,7 @@ public class Editor : Scenario } catch (Exception ex) { - MessageBox.ErrorQuery (ApplicationImpl.Instance, "Error", ex.Message, "Ok"); + MessageBox.ErrorQuery (Application.Instance, "Error", ex.Message, "Ok"); } } @@ -315,11 +315,11 @@ public class Editor : Scenario if (!found) { - MessageBox.Query (ApplicationImpl.Instance, "Find", $"The following specified text was not found: '{_textToFind}'", "Ok"); + MessageBox.Query (Application.Instance, "Find", $"The following specified text was not found: '{_textToFind}'", "Ok"); } else if (gaveFullTurn) { - MessageBox.Query (ApplicationImpl.Instance, + MessageBox.Query (Application.Instance, "Find", $"No more occurrences were found for the following specified text: '{_textToFind}'", "Ok" @@ -895,7 +895,7 @@ public class Editor : Scenario if (_textView.ReplaceAllText (_textToFind, _matchCase, _matchWholeWord, _textToReplace)) { - MessageBox.Query (ApplicationImpl.Instance, + MessageBox.Query (Application.Instance, "Replace All", $"All occurrences were replaced for the following specified text: '{_textToReplace}'", "Ok" @@ -903,7 +903,7 @@ public class Editor : Scenario } else { - MessageBox.Query (ApplicationImpl.Instance, + MessageBox.Query (Application.Instance, "Replace All", $"None of the following specified text was found: '{_textToFind}'", "Ok" @@ -1155,7 +1155,7 @@ public class Editor : Scenario { if (File.Exists (path)) { - if (MessageBox.Query (ApplicationImpl.Instance, + if (MessageBox.Query (Application.Instance, "Save File", "File already exists. Overwrite any way?", "No", @@ -1194,11 +1194,11 @@ public class Editor : Scenario _originalText = Encoding.Unicode.GetBytes (_textView.Text); _saved = true; _textView.ClearHistoryChanges (); - MessageBox.Query (ApplicationImpl.Instance, "Save File", "File was successfully saved.", "Ok"); + MessageBox.Query (Application.Instance, "Save File", "File was successfully saved.", "Ok"); } catch (Exception ex) { - MessageBox.ErrorQuery (ApplicationImpl.Instance, "Error", ex.Message, "Ok"); + MessageBox.ErrorQuery (Application.Instance, "Error", ex.Message, "Ok"); return false; } diff --git a/Examples/UICatalog/Scenarios/FileDialogExamples.cs b/Examples/UICatalog/Scenarios/FileDialogExamples.cs index 4621356f1..2d9ccbaf7 100644 --- a/Examples/UICatalog/Scenarios/FileDialogExamples.cs +++ b/Examples/UICatalog/Scenarios/FileDialogExamples.cs @@ -133,7 +133,7 @@ public class FileDialogExamples : Scenario } catch (Exception ex) { - MessageBox.ErrorQuery (ApplicationImpl.Instance, "Error", ex.ToString (), "_Ok"); + MessageBox.ErrorQuery (Application.Instance, "Error", ex.ToString (), "_Ok"); } finally { @@ -153,7 +153,7 @@ public class FileDialogExamples : Scenario { if (File.Exists (e.Dialog.Path)) { - int? result = MessageBox.Query (ApplicationImpl.Instance, "Overwrite?", "File already exists", "_Yes", "_No"); + int? result = MessageBox.Query (Application.Instance, "Overwrite?", "File already exists", "_Yes", "_No"); e.Cancel = result == 1; } } @@ -248,7 +248,7 @@ public class FileDialogExamples : Scenario if (canceled) { - MessageBox.Query (ApplicationImpl.Instance, + MessageBox.Query (Application.Instance, "Canceled", "You canceled navigation and did not pick anything", "Ok" @@ -256,7 +256,7 @@ public class FileDialogExamples : Scenario } else if (_cbAllowMultipleSelection.CheckedState == CheckState.Checked) { - MessageBox.Query (ApplicationImpl.Instance, + MessageBox.Query (Application.Instance, "Chosen!", "You chose:" + Environment.NewLine + string.Join (Environment.NewLine, multiSelected.Select (m => m)), "Ok" @@ -264,7 +264,7 @@ public class FileDialogExamples : Scenario } else { - MessageBox.Query (ApplicationImpl.Instance, + MessageBox.Query (Application.Instance, "Chosen!", "You chose:" + Environment.NewLine + path, "Ok" diff --git a/Examples/UICatalog/Scenarios/Generic.cs b/Examples/UICatalog/Scenarios/Generic.cs index a8c3c7266..a5a3b81cc 100644 --- a/Examples/UICatalog/Scenarios/Generic.cs +++ b/Examples/UICatalog/Scenarios/Generic.cs @@ -29,7 +29,7 @@ public sealed class Generic : Scenario { // When Accepting is handled, set e.Handled to true to prevent further processing. e.Handled = true; - MessageBox.ErrorQuery (ApplicationImpl.Instance, "Error", "You pressed the button!", "_Ok"); + MessageBox.ErrorQuery (Application.Instance, "Error", "You pressed the button!", "_Ok"); }; appWindow.Add (button); diff --git a/Examples/UICatalog/Scenarios/HexEditor.cs b/Examples/UICatalog/Scenarios/HexEditor.cs index fdd4b5e83..ca8728c4d 100644 --- a/Examples/UICatalog/Scenarios/HexEditor.cs +++ b/Examples/UICatalog/Scenarios/HexEditor.cs @@ -181,7 +181,7 @@ public class HexEditor : Scenario } } - private void Copy () { MessageBox.ErrorQuery (ApplicationImpl.Instance, "Not Implemented", "Functionality not yet implemented.", "Ok"); } + private void Copy () { MessageBox.ErrorQuery (Application.Instance, "Not Implemented", "Functionality not yet implemented.", "Ok"); } private void CreateDemoFile (string fileName) { @@ -208,7 +208,7 @@ public class HexEditor : Scenario ms.Close (); } - private void Cut () { MessageBox.ErrorQuery (ApplicationImpl.Instance, "Not Implemented", "Functionality not yet implemented.", "Ok"); } + private void Cut () { MessageBox.ErrorQuery (Application.Instance, "Not Implemented", "Functionality not yet implemented.", "Ok"); } private Stream LoadFile () { @@ -216,7 +216,7 @@ public class HexEditor : Scenario if (!_saved && _hexView!.Edits.Count > 0 && _hexView.Source is {}) { - if (MessageBox.ErrorQuery (ApplicationImpl.Instance, + if (MessageBox.ErrorQuery (Application.Instance, "Save", "The changes were not saved. Want to open without saving?", "_Yes", @@ -267,7 +267,7 @@ public class HexEditor : Scenario d.Dispose (); } - private void Paste () { MessageBox.ErrorQuery (ApplicationImpl.Instance, "Not Implemented", "Functionality not yet implemented.", "_Ok"); } + private void Paste () { MessageBox.ErrorQuery (Application.Instance, "Not Implemented", "Functionality not yet implemented.", "_Ok"); } private void Quit () { Application.RequestStop (); } private void Save () diff --git a/Examples/UICatalog/Scenarios/Images.cs b/Examples/UICatalog/Scenarios/Images.cs index 5791166cb..97c612674 100644 --- a/Examples/UICatalog/Scenarios/Images.cs +++ b/Examples/UICatalog/Scenarios/Images.cs @@ -183,7 +183,7 @@ public class Images : Scenario if (!_sixelSupportResult.SupportsTransparency) { - if (MessageBox.Query (ApplicationImpl.Instance, + if (MessageBox.Query (Application.Instance, "Transparency Not Supported", "It looks like your terminal does not support transparent sixel backgrounds. Do you want to try anyway?", "Yes", @@ -288,7 +288,7 @@ public class Images : Scenario } catch (Exception ex) { - MessageBox.ErrorQuery (ApplicationImpl.Instance, "Could not open file", ex.Message, "Ok"); + MessageBox.ErrorQuery (Application.Instance, "Could not open file", ex.Message, "Ok"); return; } @@ -492,7 +492,7 @@ public class Images : Scenario { if (_imageView.FullResImage == null) { - MessageBox.Query (ApplicationImpl.Instance, "No Image Loaded", "You must first open an image. Use the 'Open Image' button above.", "Ok"); + MessageBox.Query (Application.Instance, "No Image Loaded", "You must first open an image. Use the 'Open Image' button above.", "Ok"); return; } diff --git a/Examples/UICatalog/Scenarios/InteractiveTree.cs b/Examples/UICatalog/Scenarios/InteractiveTree.cs index d90af1fa4..e65aec284 100644 --- a/Examples/UICatalog/Scenarios/InteractiveTree.cs +++ b/Examples/UICatalog/Scenarios/InteractiveTree.cs @@ -173,7 +173,7 @@ public class InteractiveTree : Scenario if (parent is null) { - MessageBox.ErrorQuery (ApplicationImpl.Instance, + MessageBox.ErrorQuery (Application.Instance, "Could not delete", $"Parent of '{toDelete}' was unexpectedly null", "Ok" diff --git a/Examples/UICatalog/Scenarios/KeyBindings.cs b/Examples/UICatalog/Scenarios/KeyBindings.cs index 635aa6f6e..948cebcef 100644 --- a/Examples/UICatalog/Scenarios/KeyBindings.cs +++ b/Examples/UICatalog/Scenarios/KeyBindings.cs @@ -164,17 +164,17 @@ public class KeyBindingsDemo : View AddCommand (Command.Save, ctx => { - MessageBox.Query (ApplicationImpl.Instance, $"{ctx.Command}", $"Ctx: {ctx}", buttons: "Ok"); + MessageBox.Query (Application.Instance, $"{ctx.Command}", $"Ctx: {ctx}", buttons: "Ok"); return true; }); AddCommand (Command.New, ctx => { - MessageBox.Query (ApplicationImpl.Instance, $"{ctx.Command}", $"Ctx: {ctx}", buttons: "Ok"); + MessageBox.Query (Application.Instance, $"{ctx.Command}", $"Ctx: {ctx}", buttons: "Ok"); return true; }); AddCommand (Command.HotKey, ctx => { - MessageBox.Query (ApplicationImpl.Instance, $"{ctx.Command}", $"Ctx: {ctx}\nCommand: {ctx.Command}", buttons: "Ok"); + MessageBox.Query (Application.Instance, $"{ctx.Command}", $"Ctx: {ctx}\nCommand: {ctx.Command}", buttons: "Ok"); SetFocus (); return true; }); @@ -189,7 +189,7 @@ public class KeyBindingsDemo : View { return false; } - MessageBox.Query (ApplicationImpl.Instance, $"{keyCommandContext.Binding}", $"Key: {keyCommandContext.Binding.Key}\nCommand: {ctx.Command}", buttons: "Ok"); + MessageBox.Query (Application.Instance, $"{keyCommandContext.Binding}", $"Key: {keyCommandContext.Binding.Key}\nCommand: {ctx.Command}", buttons: "Ok"); Application.RequestStop (); return true; }); diff --git a/Examples/UICatalog/Scenarios/ListColumns.cs b/Examples/UICatalog/Scenarios/ListColumns.cs index d300b4163..c19e9c3b9 100644 --- a/Examples/UICatalog/Scenarios/ListColumns.cs +++ b/Examples/UICatalog/Scenarios/ListColumns.cs @@ -336,7 +336,7 @@ public class ListColumns : Scenario } catch (Exception ex) { - MessageBox.ErrorQuery (ApplicationImpl.Instance, 60, 20, "Failed to set", ex.Message, "Ok"); + MessageBox.ErrorQuery (Application.Instance, 60, 20, "Failed to set", ex.Message, "Ok"); } } } diff --git a/Examples/UICatalog/Scenarios/MessageBoxes.cs b/Examples/UICatalog/Scenarios/MessageBoxes.cs index fcb6488ee..6c691516d 100644 --- a/Examples/UICatalog/Scenarios/MessageBoxes.cs +++ b/Examples/UICatalog/Scenarios/MessageBoxes.cs @@ -251,7 +251,7 @@ public class MessageBoxes : Scenario { buttonPressedLabel.Text = $"{MessageBox.Query ( - ApplicationImpl.Instance, width, + Application.Instance, width, height, titleEdit.Text, messageEdit.Text, @@ -263,7 +263,7 @@ public class MessageBoxes : Scenario else { buttonPressedLabel.Text = - $"{MessageBox.ErrorQuery (ApplicationImpl.Instance, + $"{MessageBox.ErrorQuery (Application.Instance, width, height, titleEdit.Text, diff --git a/Examples/UICatalog/Scenarios/MultiColouredTable.cs b/Examples/UICatalog/Scenarios/MultiColouredTable.cs index 5bac4e125..b5bf47d48 100644 --- a/Examples/UICatalog/Scenarios/MultiColouredTable.cs +++ b/Examples/UICatalog/Scenarios/MultiColouredTable.cs @@ -99,7 +99,7 @@ public class MultiColouredTable : Scenario } catch (Exception ex) { - MessageBox.ErrorQuery (ApplicationImpl.Instance, 60, 20, "Failed to set text", ex.Message, "Ok"); + MessageBox.ErrorQuery (Application.Instance, 60, 20, "Failed to set text", ex.Message, "Ok"); } _tableView.Update (); diff --git a/Examples/UICatalog/Scenarios/Navigation.cs b/Examples/UICatalog/Scenarios/Navigation.cs index 7c78ef359..899dc22ee 100644 --- a/Examples/UICatalog/Scenarios/Navigation.cs +++ b/Examples/UICatalog/Scenarios/Navigation.cs @@ -59,7 +59,7 @@ public class Navigation : Scenario Y = 0, Title = $"TopButton _{GetNextHotKey ()}" }; - button.Accepting += (sender, args) => MessageBox.Query (ApplicationImpl.Instance, "hi", button.Title, "_Ok"); + button.Accepting += (sender, args) => MessageBox.Query (Application.Instance, "hi", button.Title, "_Ok"); testFrame.Add (button); diff --git a/Examples/UICatalog/Scenarios/Notepad.cs b/Examples/UICatalog/Scenarios/Notepad.cs index f597f2fad..3c77b6192 100644 --- a/Examples/UICatalog/Scenarios/Notepad.cs +++ b/Examples/UICatalog/Scenarios/Notepad.cs @@ -71,7 +71,7 @@ public class Notepad : Scenario new MenuItem { Title = "_About", - Action = () => MessageBox.Query (ApplicationImpl.Instance, "Notepad", "About Notepad...", "Ok") + Action = () => MessageBox.Query (Application.Instance, "Notepad", "About Notepad...", "Ok") } ] ) @@ -196,7 +196,7 @@ public class Notepad : Scenario if (tab.UnsavedChanges) { - int? result = MessageBox.Query (ApplicationImpl.Instance, + int? result = MessageBox.Query (Application.Instance, "Save Changes", $"Save changes to {tab.Text.TrimEnd ('*')}", "Yes", diff --git a/Examples/UICatalog/Scenarios/RuneWidthGreaterThanOne.cs b/Examples/UICatalog/Scenarios/RuneWidthGreaterThanOne.cs index 23f2e63fa..18f511ade 100644 --- a/Examples/UICatalog/Scenarios/RuneWidthGreaterThanOne.cs +++ b/Examples/UICatalog/Scenarios/RuneWidthGreaterThanOne.cs @@ -166,7 +166,7 @@ public class RuneWidthGreaterThanOne : Scenario { if (_text is { }) { - MessageBox.Query (ApplicationImpl.Instance, "Say Hello 你", $"Hello {_text.Text}", "Ok"); + MessageBox.Query (Application.Instance, "Say Hello 你", $"Hello {_text.Text}", "Ok"); } } @@ -197,7 +197,7 @@ public class RuneWidthGreaterThanOne : Scenario { if (_text is { }) { - MessageBox.Query (ApplicationImpl.Instance, "Say Hello", $"Hello {_text.Text}", "Ok"); + MessageBox.Query (Application.Instance, "Say Hello", $"Hello {_text.Text}", "Ok"); } } @@ -252,7 +252,7 @@ public class RuneWidthGreaterThanOne : Scenario { if (_text is { }) { - MessageBox.Query (ApplicationImpl.Instance, "こんにちはと言う", $"こんにちは {_text.Text}", "Ok"); + MessageBox.Query (Application.Instance, "こんにちはと言う", $"こんにちは {_text.Text}", "Ok"); } } diff --git a/Examples/UICatalog/Scenarios/WindowsAndFrameViews.cs b/Examples/UICatalog/Scenarios/WindowsAndFrameViews.cs index 4404f8008..bfcaee6d2 100644 --- a/Examples/UICatalog/Scenarios/WindowsAndFrameViews.cs +++ b/Examples/UICatalog/Scenarios/WindowsAndFrameViews.cs @@ -18,7 +18,7 @@ public class WindowsAndFrameViews : Scenario static int? About () { - return MessageBox.Query (ApplicationImpl.Instance, + return MessageBox.Query (Application.Instance, "About UI Catalog", "UI Catalog is a comprehensive sample library for Terminal.Gui", "Ok" diff --git a/Examples/UICatalog/Scenarios/WizardAsView.cs b/Examples/UICatalog/Scenarios/WizardAsView.cs index 6ce39b6f0..c3d7266d3 100644 --- a/Examples/UICatalog/Scenarios/WizardAsView.cs +++ b/Examples/UICatalog/Scenarios/WizardAsView.cs @@ -21,7 +21,7 @@ public class WizardAsView : Scenario { Title = "_Restart Configuration...", Action = () => MessageBox.Query ( - ApplicationImpl.Instance, + Application.Instance, "Wizard", "Are you sure you want to reset the Wizard and start over?", "Ok", @@ -32,7 +32,7 @@ public class WizardAsView : Scenario { Title = "Re_boot Server...", Action = () => MessageBox.Query ( - ApplicationImpl.Instance, + Application.Instance, "Wizard", "Are you sure you want to reboot the server start over?", "Ok", @@ -43,7 +43,7 @@ public class WizardAsView : Scenario { Title = "_Shutdown Server...", Action = () => MessageBox.Query ( - ApplicationImpl.Instance, + Application.Instance, "Wizard", "Are you sure you want to cancel setup and shutdown?", "Ok", diff --git a/Terminal.Gui/App/Application.Lifecycle.cs b/Terminal.Gui/App/Application.Lifecycle.cs index 5262376ed..9fbc9fba1 100644 --- a/Terminal.Gui/App/Application.Lifecycle.cs +++ b/Terminal.Gui/App/Application.Lifecycle.cs @@ -10,6 +10,22 @@ namespace Terminal.Gui.App; public static partial class Application // Lifecycle (Init/Shutdown) { + /// + /// Gets the singleton instance used by the legacy static Application model. + /// + /// + /// + /// For new code, prefer using to get an instance-based application. + /// This property is provided for backward compatibility and internal use. + /// + /// + /// This property returns the same singleton instance used by the legacy static + /// methods like and . + /// + /// + [Obsolete ("The legacy static Application object is going away. Use Application.Create() for new code.")] + public static IApplication Instance => ApplicationImpl.Instance; + /// /// Creates a new instance. /// @@ -49,7 +65,7 @@ public static partial class Application // Lifecycle (Init/Shutdown) internal set => ApplicationImpl.Instance.MainThreadId = value; } - /// + /// [Obsolete ("The legacy static Application object is going away.")] public static void Shutdown () => ApplicationImpl.Instance.Dispose (); diff --git a/Terminal.Gui/App/Application.cd b/Terminal.Gui/App/Application.cd deleted file mode 100644 index 49b0c85cb..000000000 --- a/Terminal.Gui/App/Application.cd +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - gEK4FIgQOAQIuhQeBwoUgSCgAAJL0AACESIKoAiBWw8= - App\Application.cs - - - - - - AABAAAAAAABCAAAAAAAAAAAAAAAAIgIAAAAAAAAAAAA= - App\ApplicationNavigation.cs - - - - - - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= - App\IterationEventArgs.cs - - - - - - AAAAAAAAACAAAAAAAAAAAAAACBAAEAAIIAIAgAAAEAI= - App\MainLoop.cs - - - - - - - AAAAAgAAAAAAAAAAAEAAAAAACAAAAAAAAAAAAAAAAAA= - App\MainLoopSyncContext.cs - - - - - - AAAAAAAAACACAgAAAAAAAAAAAAAAAAACQAAAAAAAAAA= - App\SessionToken.cs - - - - - - - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA= - App\SessionTokenEventArgs.cs - - - - - - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAQAA= - App\Timeout.cs - - - - - - AAAAAAAAAAAAAAAAAAAAAAAAAAAACAIAAAAAAAAAAAA= - App\TimeoutEventArgs.cs - - - - - - AABgAAAAIAAIAgQUAAAAAQAAAAAAAAAAQAAKgAAAEAI= - App\ApplicationImpl.cs - - - - - - - BAAgAAAAgABAAoAAAAAAABAAACEAAAAAAABAAgAAAAA= - App\MouseGrabHandler.cs - - - - - - - AAAAAAAACAAAAAQAAAAABAAAAAAAEAAAAAAAAAAAAAA= - App\MainLoop.cs - - - - - - AAAgAAAAAAAIAgQUAAAAAQAAAAAAAAAAAAAKgAAAEAI= - App\IApplication.cs - - - - - - - - - - BAAgAAAAAAAAAgAAAAAAABAAACEAAAAAAAAAAgAAAAA= - App\IMouseGrabHandler.cs - - - - - - BAAAIAAAAQAAAAAQACAAAIBAAQAAAAAAAAAIgAAAAAA= - App\ITimedEvents.cs - - - - \ No newline at end of file diff --git a/Terminal.Gui/App/ApplicationImpl.Driver.cs b/Terminal.Gui/App/ApplicationImpl.Driver.cs index edb6adcbd..11fabb91a 100644 --- a/Terminal.Gui/App/ApplicationImpl.Driver.cs +++ b/Terminal.Gui/App/ApplicationImpl.Driver.cs @@ -2,7 +2,7 @@ using System.Collections.Concurrent; namespace Terminal.Gui.App; -public partial class ApplicationImpl +internal partial class ApplicationImpl { /// public IDriver? Driver { get; set; } diff --git a/Terminal.Gui/App/ApplicationImpl.Lifecycle.cs b/Terminal.Gui/App/ApplicationImpl.Lifecycle.cs index 7cb701605..acdd2a0cf 100644 --- a/Terminal.Gui/App/ApplicationImpl.Lifecycle.cs +++ b/Terminal.Gui/App/ApplicationImpl.Lifecycle.cs @@ -3,7 +3,7 @@ using System.Diagnostics.CodeAnalysis; namespace Terminal.Gui.App; -public partial class ApplicationImpl +internal partial class ApplicationImpl { /// public int? MainThreadId { get; set; } diff --git a/Terminal.Gui/App/ApplicationImpl.Run.cs b/Terminal.Gui/App/ApplicationImpl.Run.cs index 45ad59fab..1e037fee2 100644 --- a/Terminal.Gui/App/ApplicationImpl.Run.cs +++ b/Terminal.Gui/App/ApplicationImpl.Run.cs @@ -3,7 +3,7 @@ using System.Diagnostics.CodeAnalysis; namespace Terminal.Gui.App; -public partial class ApplicationImpl +internal partial class ApplicationImpl { // Lock object to protect session stack operations and cached state updates private readonly object _sessionStackLock = new (); diff --git a/Terminal.Gui/App/ApplicationImpl.Screen.cs b/Terminal.Gui/App/ApplicationImpl.Screen.cs index 07be5ff10..cd7c66cfc 100644 --- a/Terminal.Gui/App/ApplicationImpl.Screen.cs +++ b/Terminal.Gui/App/ApplicationImpl.Screen.cs @@ -1,7 +1,7 @@ namespace Terminal.Gui.App; -public partial class ApplicationImpl +internal partial class ApplicationImpl { /// public event EventHandler>? ScreenChanged; diff --git a/Terminal.Gui/App/ApplicationImpl.cs b/Terminal.Gui/App/ApplicationImpl.cs index b92f388a2..9910e7019 100644 --- a/Terminal.Gui/App/ApplicationImpl.cs +++ b/Terminal.Gui/App/ApplicationImpl.cs @@ -6,7 +6,7 @@ namespace Terminal.Gui.App; /// Implementation of core methods using the modern /// main loop architecture with component factories for different platforms. /// -public partial class ApplicationImpl : IApplication +internal partial class ApplicationImpl : IApplication { /// /// INTERNAL: Creates a new instance of the Application backend and subscribes to Application configuration property @@ -75,7 +75,7 @@ public partial class ApplicationImpl : IApplication /// /// Gets the currently configured backend implementation of gateway methods. /// - public static IApplication Instance + internal static IApplication Instance { get { diff --git a/Terminal.Gui/Views/Autocomplete/Autocomplete.cd b/Terminal.Gui/Views/Autocomplete/Autocomplete.cd deleted file mode 100644 index 8b0f16b4f..000000000 --- a/Terminal.Gui/Views/Autocomplete/Autocomplete.cd +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - AAAgAABAAQIAAAAAAAAAAAAABAAAIAQAgAEIAggAIAA= - Core\Autocomplete\AppendAutocomplete.cs - - - - - - AAQgAAAAAUAAIAAAAAIAAAAAAAEAIAQIgQAIQAAAMBA= - Core\Autocomplete\AutocompleteBase.cs - - - - - - - - - Core\Autocomplete\PopupAutocomplete.cs - - - - - IAEhAAQAASBEQAAAAAIBAAgYAAAAIAwAwKAAQACBAAA= - Core\Autocomplete\PopupAutocomplete.cs - - - - - - CEAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAIAA= - Core\Autocomplete\SingleWordSuggestionGenerator.cs - - - - - - - AAAAAAAAAAAAAEAAAAAABAAAAAAAAAAAAAAAAAAAAAE= - Core\Autocomplete\Suggestion.cs - - - - - - AAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAgAAAAAAAAAA= - Views\TextField.cs - - - - - - AAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAgAAAAAAAAAA= - Views\TextView.cs - - - - - - AAQgAAAAAUAAIAAAAAAAAAAAAAEAIAQIgQAIQAAAMBA= - Core\Autocomplete\IAutocomplete.cs - - - - - - - - - AEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAA= - Core\Autocomplete\ISuggestionGenerator.cs - - - - \ No newline at end of file diff --git a/Terminal.Gui/Views/CollectionNavigation/CollectionNavigation.cd b/Terminal.Gui/Views/CollectionNavigation/CollectionNavigation.cd deleted file mode 100644 index 02bb4adb1..000000000 --- a/Terminal.Gui/Views/CollectionNavigation/CollectionNavigation.cd +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - - - - - - - - - - AAgEAAAAAAAQAAAIAAEAAgAAAAAABAAEAAAAACwAAAA= - Views\CollectionNavigation\CollectionNavigatorBase.cs - - - - - - - - - - AAAAAAAAAAAAQAAAAAAAAgAAAAAAAAAEAAAAAAAAAAA= - Views\CollectionNavigation\CollectionNavigator.cs - - - - - - - AAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAQA= - Views\CollectionNavigation\DefaultCollectionNavigatorMatcher.cs - - - - - - - AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAEAAAAIAAAAAA= - Views\CollectionNavigation\TableCollectionNavigator.cs - - - - - - AAE+ASAkEnAAABAAKGAggYAZJAIAABEAcBAaAwAQIAA= - Views\ListView.cs - - - - - - - - - - QwUeAxwgICIAcABIABeR0oBAkhoFGGOBDABgAN3oPEI= - Views\TableView\TableView.cs - - - - - - - AAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAA= - Views\TreeView\TreeView.cs - - - - - - - UwAGySBgBSBGMAQgIiCaBDUItJIBSAWwRMQOSgQCwJI= - Views\TreeView\TreeView.cs - - - - - - - - - - - - - iIY4LQFUHDKVIHIESBoigQcFT6GxhBDABGJItBQAwAQ= - Views\FileDialogs\FileDialog.cs - - - - - - - AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAA= - Views\FileDialogs\FileDialogCollectionNavigator.cs - - - - - - AAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAA= - Views\CollectionNavigation\ICollectionNavigatorMatcher.cs - - - - - - AAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= - Views\CollectionNavigation\IListCollectionNavigator.cs - - - - - - AAgAAAAAAAAAAAAIAAAAAAAAAAAABAAAAAAAACgAAAA= - Views\CollectionNavigation\ICollectionNavigator.cs - - - - \ No newline at end of file diff --git a/Terminal.Gui/Views/FileDialogs/FileDialog.cd b/Terminal.Gui/Views/FileDialogs/FileDialog.cd deleted file mode 100644 index 22893eb03..000000000 --- a/Terminal.Gui/Views/FileDialogs/FileDialog.cd +++ /dev/null @@ -1,168 +0,0 @@ - - - - - - AAACAAAAAAgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= - FileServices\DefaultSearchMatcher.cs - - - - - - - ABAIQAIIIAAAAAACQAAAAIQAAAQAAIAAAQAAAAAIAAI= - FileServices\FileSystemInfoStats.cs - - - - - - - - - - - - - Views\FileDialogs\FileDialog.cs - - - - - iIY4LQFUHDKVIHIESBoigQcFT6GxhBDABGJItBQAwAQ= - Views\FileDialogs\FileDialog.cs - - - - - - - GgBAAAFHAAAAuAAAAAAAEAQQBYAAKREAAAAYQCCAAAA= - Views\FileDialogs\FileDialogStyle.cs - - - - - - - - - AAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAA= - Views\FileDialogs\FilesSelectedEventArgs.cs - - - - - - AQABAgEAAAAAAAAAIACAAAAAAAAAAQAAAAAAAAAADAI= - Views\FileDialogs\FileDialogHistory.cs - - - - - - AABAABAAAAAAAAIAAAAEQAAAAAAAQAAAAgAAAAAAAAI= - Views\FileDialogs\FileDialogState.cs - - - - - - - - - AAAAAAAAAAAgAAAEAAAAAAAAAAAAAAAAAAgAAAAABAA= - Views\FileDialogs\AllowedType.cs - - - - - - - AAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAABAA= - Views\FileDialogs\AllowedType.cs - - - - - - - - - - AIACAAABQAAAAAAAAAAACAAAIACAAAAAAAIAAAAAAAA= - Text\NerdFonts.cs - - - - - - EAAAAAAAAAAAAAAAAAAABAAwAAAAQAAAAABAAAAACAA= - FileServices\FileSystemTreeBuilder.cs - - - - - - - AAAAAAAAAAAAACAAAAAAACAAAAEAAAAAAAAAAAAAgAA= - Views\FileDialogs\DefaultFileOperations.cs - - - - - - - AgAAAAAAAEAAAAAAAAAAAAEAAAAAAACAAAAAAAAAAAA= - FileServices\FileSystemColorProvider.cs - - - - - - ABAAAAAAAACAQAAAAAAAAEAgAAAAAQAAAAAAAAAAAiA= - FileServices\FileSystemIconProvider.cs - - - - - - - - - AQAAAAAAIAACAEAACAAAAAACAAAEAAAEAAAAgAgBBAA= - Views\FileDialogs\FileDialogTableSource.cs - - - - - - - AAAAAAAAAAAAACAAAAAAAAAAAAEAAAAAAAAAAAAAgAA= - FileServices\IFileOperations.cs - - - - - - AAACAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= - FileServices\ISearchMatcher.cs - - - - - - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAA= - Views\FileDialogs\AllowedType.cs - - - - - - AAAAABAAAAAAACAAAAAAAAAAAAAEAAAAAAAAAAAAAAA= - Views\FileDialogs\OpenMode.cs - - - - \ No newline at end of file diff --git a/Terminal.Gui/Views/MessageBox.cs b/Terminal.Gui/Views/MessageBox.cs index e6d3cebd3..07fccc069 100644 --- a/Terminal.Gui/Views/MessageBox.cs +++ b/Terminal.Gui/Views/MessageBox.cs @@ -18,7 +18,7 @@ namespace Terminal.Gui.Views; /// Important: All MessageBox methods require an instance to be passed. /// This enables proper modal dialog management and respects the application's lifecycle. Pass your /// application instance (from ) or use the legacy -/// if using the static Application pattern. +/// if using the static Application pattern. /// /// /// Example using instance-based pattern: diff --git a/Terminal.Gui/Views/Shortcut.cs b/Terminal.Gui/Views/Shortcut.cs index 561fb4bae..65d24ea8b 100644 --- a/Terminal.Gui/Views/Shortcut.cs +++ b/Terminal.Gui/Views/Shortcut.cs @@ -634,7 +634,7 @@ public class Shortcut : View, IOrientation, IDesignable get => _bindKeyToApplication; set { - App ??= SuperView?.App ?? ApplicationImpl.Instance; // HACK: Remove once legacy static Application is gone + App ??= SuperView?.App ?? Application.Instance; // HACK: Remove once legacy static Application is gone Debug.Assert (App is { }); if (value == _bindKeyToApplication) diff --git a/Tests/TerminalGuiFluentTesting/ClassDiagram1.cd b/Tests/TerminalGuiFluentTesting/ClassDiagram1.cd deleted file mode 100644 index 7fe4c3b91..000000000 --- a/Tests/TerminalGuiFluentTesting/ClassDiagram1.cd +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAIAAAAAA= - With.cs - - - - - - AQAAAAAAACAAAQEAAAAgAAAAAAAAAAAAAAAAAAAAAAI= - FakeInput.cs - - - - - - - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= - FakeNetInput.cs - - - - - - - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= - FakeWindowsInput.cs - - - - - - - AAAAAAAAgCAAgAAAAAAAAAAAAAAAQAAAMAAAAAEAAAA= - FakeOutput.cs - - - - - - - - - - ABJAAAIAACBACRAAg4IAAAAgAJIEgQQAKACIBACAIgI= - GuiTestContext.cs - - - - - - - - - - - - AAAAAIAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= - TextWriterLoggerProvider.cs - - - - - - - AAAAAAAAAAAAAAAAAAEAAAAAAAgAAAAAAAAIAAAAAAA= - TextWriterLogger.cs - - - - - - - AAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAACACAAAAAAgI= - NetSequences.cs - - - - - - AAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAACAAA= - V2TestDriver.cs - - - - \ No newline at end of file diff --git a/docfx/docs/application.md b/docfx/docs/application.md index 369488621..634e2ae60 100644 --- a/docfx/docs/application.md +++ b/docfx/docs/application.md @@ -18,7 +18,7 @@ Terminal.Gui v2 uses an instance-based application architecture with the **IRunn graph TB subgraph ViewTree["View Hierarchy (SuperView/SubView)"] direction TB - Top[app.Current
Window] + Top[app.TopRunnable
Window] Menu[MenuBar] Status[StatusBar] Content[Content View] @@ -59,19 +59,19 @@ sequenceDiagram App->>Main: Run(mainWindow) activate Main - Note over App: SessionStack: [Main]
Current: Main + Note over App: SessionStack: [Main]
TopRunnable: Main Main->>Dialog: Run(dialog) activate Dialog - Note over App: SessionStack: [Dialog, Main]
Current: Dialog + Note over App: SessionStack: [Dialog, Main]
TopRunnable: Dialog Dialog->>App: RequestStop() deactivate Dialog - Note over App: SessionStack: [Main]
Current: Main + Note over App: SessionStack: [Main]
TopRunnable: Main Main->>App: RequestStop() deactivate Main - Note over App: SessionStack: []
Current: null + Note over App: SessionStack: []
TopRunnable: null ``` ## Key Concepts @@ -82,37 +82,37 @@ sequenceDiagram ```csharp // OLD (v1 / early v2 - still works but obsolete): -Application.Init(); -var top = new Window(); -top.Add(myView); -Application.Run(top); -top.Dispose(); -Application.Shutdown(); // Obsolete - use Dispose() instead +Application.Init (); +Window top = new (); +top.Add (myView); +Application.Run (top); +top.Dispose (); +Application.Shutdown (); // Obsolete - use Dispose() instead // RECOMMENDED (v2 - instance-based with using statement): -using (var app = Application.Create().Init()) +using (IApplication app = Application.Create ().Init ()) { - var top = new Window(); - top.Add(myView); - app.Run(top); - top.Dispose(); + Window top = new (); + top.Add (myView); + app.Run (top); + top.Dispose (); } // app.Dispose() called automatically // WITH IRunnable (fluent API with automatic disposal): -using (var app = Application.Create().Init()) +using (IApplication app = Application.Create ().Init ()) { - app.Run(); - Color? result = app.GetResult(); + app.Run (); + Color? result = app.GetResult (); } // SIMPLEST (manual disposal): -var app = Application.Create().Init(); -app.Run(); -Color? result = app.GetResult(); -app.Dispose(); +IApplication app = Application.Create ().Init (); +app.Run (); +Color? result = app.GetResult (); +app.Dispose (); ``` -**Note:** The static `Application` class delegates to `ApplicationImpl.Instance` (a singleton). `Application.Create()` creates a **new** `ApplicationImpl` instance, enabling multiple application contexts and better testability. +**Note:** The static `Application` class delegates to a singleton instance accessible via `Application.Instance`. `Application.Create()` creates a **new** application instance, enabling multiple application contexts and better testability. ### View.App Property @@ -130,7 +130,7 @@ public class View /// Gets the application context, checking parent hierarchy if needed. /// Override to customize application resolution. ///
- public virtual IApplication? GetApp() => App ?? SuperView?.GetApp(); + public virtual IApplication? GetApp () => App ?? SuperView?.GetApp (); } ``` @@ -147,10 +147,10 @@ public class View ```csharp public class MyView : View { - public override void OnEnter(View view) + public override void OnEnter (View view) { // Use View.App instead of static Application - App?.Current?.SetNeedsDraw(); + App?.TopRunnable?.SetNeedsDraw (); // Access SessionStack if (App?.SessionStack.Count > 0) @@ -168,15 +168,15 @@ public class MyView : View { private readonly IApplication _app; - public MyView(IApplication app) + public MyView (IApplication app) { _app = app; // Now completely decoupled from static Application } - public void DoWork() + public void DoWork () { - _app.Current?.SetNeedsDraw(); + _app.TopRunnable?.SetNeedsDraw (); } } ``` @@ -199,26 +199,26 @@ The fluent API enables elegant method chaining with automatic resource managemen ```csharp // Recommended: using statement with GetResult -using (var app = Application.Create().Init()) +using (IApplication app = Application.Create ().Init ()) { - app.Run(); - Color? result = app.GetResult(); + app.Run (); + Color? result = app.GetResult (); if (result is { }) { - ApplyColor(result); + ApplyColor (result); } } // Alternative: Manual disposal -var app = Application.Create().Init(); -app.Run(); -Color? result = app.GetResult(); -app.Dispose(); +IApplication app = Application.Create ().Init (); +app.Run (); +Color? result = app.GetResult (); +app.Dispose (); if (result is { }) { - ApplyColor(result); + ApplyColor (result); } ``` @@ -240,19 +240,19 @@ if (result is { }) ```csharp // Framework ownership - automatic disposal -using (var app = Application.Create().Init()) +using (IApplication app = Application.Create ().Init ()) { - app.Run(); // Dialog disposed automatically when Run returns - var result = app.GetResult(); + app.Run (); // Dialog disposed automatically when Run returns + MyResultType? result = app.GetResult (); } // Caller ownership - manual disposal -using (var app = Application.Create().Init()) +using (IApplication app = Application.Create ().Init ()) { - var dialog = new MyDialog(); - app.Run(dialog); - var result = dialog.Result; - dialog.Dispose(); // Caller must dispose + MyDialog dialog = new (); + app.Run (dialog); + MyResultType? result = dialog.Result; + dialog.Dispose (); // Caller must dispose } ``` @@ -265,35 +265,36 @@ public class FileDialog : Runnable { private TextField _pathField; - public FileDialog() + public FileDialog () { Title = "Select File"; - _pathField = new TextField { X = 1, Y = 1, Width = Dim.Fill(1) }; + _pathField = new () { X = 1, Y = 1, Width = Dim.Fill (1) }; - var okButton = new Button { Text = "OK", IsDefault = true }; - okButton.Accepting += (s, e) => { + Button okButton = new () { Text = "OK", IsDefault = true }; + okButton.Accepting += (s, e) => + { Result = _pathField.Text; - Application.RequestStop(); + Application.RequestStop (); }; - Add(_pathField, okButton); + Add (_pathField, okButton); } - protected override bool OnIsRunningChanging(bool oldValue, bool newValue) + protected override bool OnIsRunningChanging (bool oldValue, bool newValue) { if (!newValue) // Stopping - extract result before disposal { Result = _pathField?.Text; } - return base.OnIsRunningChanging(oldValue, newValue); + return base.OnIsRunningChanging (oldValue, newValue); } } ``` ### Lifecycle Properties -- **`IsRunning`** - True when runnable is on `RunnableSessionStack` +- **`IsRunning`** - True when runnable is on `SessionStack` - **`IsModal`** - True when runnable is at top of stack (capturing all input) - **`Result`** - Typed result value set before stopping @@ -310,7 +311,7 @@ All events follow Terminal.Gui's Cancellable Work Pattern: **Example - Result Extraction:** ```csharp -protected override bool OnIsRunningChanging(bool oldValue, bool newValue) +protected override bool OnIsRunningChanging (bool oldValue, bool newValue) { if (!newValue) // Stopping { @@ -318,33 +319,39 @@ protected override bool OnIsRunningChanging(bool oldValue, bool newValue) Result = _colorPicker.SelectedColor; // Optionally cancel stop (e.g., unsaved changes) - if (HasUnsavedChanges()) + if (HasUnsavedChanges ()) { - int response = MessageBox.Query("Save?", "Save changes?", "Yes", "No", "Cancel"); - if (response == 2) return true; // Cancel stop - if (response == 0) Save(); + var response = MessageBox.Query ("Save?", "Save changes?", "Yes", "No", "Cancel"); + if (response == 2) + { + return true; // Cancel stop + } + if (response == 0) + { + Save (); + } } } - return base.OnIsRunningChanging(oldValue, newValue); + return base.OnIsRunningChanging (oldValue, newValue); } ``` -### RunnableSessionStack +### SessionStack -The `RunnableSessionStack` manages all running `IRunnable` sessions: +The `SessionStack` manages all running `IRunnable` sessions: ```csharp public interface IApplication { /// /// Stack of running IRunnable sessions. - /// Each entry is a RunnableSessionToken wrapping an IRunnable. + /// Each entry is a SessionToken wrapping an IRunnable. /// - ConcurrentStack? RunnableSessionStack { get; } + ConcurrentStack? SessionStack { get; } /// - /// The IRunnable at the top of RunnableSessionStack (currently modal). + /// The IRunnable at the top of SessionStack (currently modal). /// IRunnable? TopRunnable { get; } } @@ -353,9 +360,9 @@ public interface IApplication **Stack Behavior:** - Push: `Begin(IRunnable)` adds to top of stack -- Pop: `End(RunnableSessionToken)` removes from stack +- Pop: `End(SessionToken)` removes from stack - Peek: `TopRunnable` returns current modal runnable -- All: `RunnableSessionStack` enumerates all running sessions +- All: `SessionStack` enumerates all running sessions ## IApplication Interface @@ -374,19 +381,19 @@ public interface IApplication IMainLoopCoordinator? Coordinator { get; } // Fluent API methods - IApplication Init(string? driverName = null); - void Dispose(); // IDisposable + IApplication Init (string? driverName = null); + void Dispose (); // IDisposable // Runnable methods - SessionToken? Begin(IRunnable runnable); - object? Run(IRunnable runnable, Func? errorHandler = null); - IApplication Run(Func? errorHandler = null) where TRunnable : IRunnable, new(); - void RequestStop(IRunnable? runnable); - void End(SessionToken sessionToken); + SessionToken? Begin (IRunnable runnable); + object? Run (IRunnable runnable, Func? errorHandler = null); + IApplication Run (Func? errorHandler = null) where TRunnable : IRunnable, new(); + void RequestStop (IRunnable? runnable); + void End (SessionToken sessionToken); // Result extraction - object? GetResult(); - T? GetResult() where T : class; + object? GetResult (); + T? GetResult () where T : class; // ... other members } @@ -423,13 +430,13 @@ The `SessionStack` property is the stack of running sessions: ```csharp // Access all running sessions -foreach (var runnable in app.SessionStack) +foreach (SessionToken runnable in app.SessionStack) { // Process each session } // From within a view -int sessionCount = App?.SessionStack.Count ?? 0; +var sessionCount = App?.SessionStack.Count ?? 0; ``` **Why "SessionStack" instead of "Runnables"?** @@ -439,25 +446,25 @@ int sessionCount = App?.SessionStack.Count ?? 0; ## Migration from Static Application -The static `Application` class delegates to `ApplicationImpl.Instance` (a singleton) and is marked obsolete. All static methods and properties are marked with `[Obsolete]` but remain functional for backward compatibility: +The static `Application` class delegates to a singleton instance and is marked obsolete. All static methods and properties are marked with `[Obsolete]` but remain functional for backward compatibility: ```csharp public static partial class Application { - [Obsolete("The legacy static Application object is going away.")] - public static View? TopRunnableView => ApplicationImpl.Instance.TopRunnableView; + [Obsolete ("The legacy static Application object is going away.")] + public static View? TopRunnableView => Instance.TopRunnableView; - [Obsolete("The legacy static Application object is going away.")] - public static IRunnable? TopRunnable => ApplicationImpl.Instance.TopRunnable; + [Obsolete ("The legacy static Application object is going away.")] + public static IRunnable? TopRunnable => Instance.TopRunnable; - [Obsolete("The legacy static Application object is going away.")] - public static ConcurrentStack? SessionStack => ApplicationImpl.Instance.SessionStack; + [Obsolete ("The legacy static Application object is going away.")] + public static ConcurrentStack? SessionStack => Instance.SessionStack; // ... other obsolete static members } ``` -**Important:** The static `Application` class uses a singleton (`ApplicationImpl.Instance`), while `Application.Create()` creates new instances. For new code, prefer the instance-based pattern using `Application.Create()`. +**Important:** The static `Application` class uses a singleton (`Application.Instance`), while `Application.Create()` creates new instances. For new code, prefer the instance-based pattern using `Application.Create()`. ### Migration Strategies @@ -465,15 +472,15 @@ public static partial class Application ```csharp // OLD: -void MyMethod() +void MyMethod () { - Application.TopRunnable?.SetNeedsDraw(); + Application.TopRunnable?.SetNeedsDraw (); } // NEW: -void MyMethod(View view) +void MyMethod (View view) { - view.App?.TopRunnableView?.SetNeedsDraw(); + view.App?.TopRunnableView?.SetNeedsDraw (); } ``` @@ -481,18 +488,18 @@ void MyMethod(View view) ```csharp // OLD: -void ProcessSessions() +void ProcessSessions () { - foreach (var runnable in Application.SessionStack) + foreach (SessionToken runnable in Application.SessionStack) { // Process } } // NEW: -void ProcessSessions(IApplication app) +void ProcessSessions (IApplication app) { - foreach (var runnable in app.SessionStack) + foreach (SessionToken runnable in app.SessionStack) { // Process } @@ -506,14 +513,14 @@ public class MyService { private readonly IApplication _app; - public MyService(IApplication app) + public MyService (IApplication app) { _app = app; } - public void DoWork() + public void DoWork () { - _app.Current?.Title = "Processing..."; + _app.TopRunnable?.Title = "Processing..."; } } ``` @@ -530,9 +537,9 @@ Terminal.Gui v2 implements the `IDisposable` pattern for proper resource cleanup ```csharp // Automatic disposal with using statement -using (var app = Application.Create().Init()) +using (IApplication app = Application.Create ().Init ()) { - app.Run(); + app.Run (); // app.Dispose() automatically called when scope exits } ``` @@ -541,15 +548,15 @@ using (var app = Application.Create().Init()) ```csharp // Manual disposal -var app = Application.Create(); +IApplication app = Application.Create (); try { - app.Init(); - app.Run(); + app.Init (); + app.Run (); } finally { - app.Dispose(); // Ensure cleanup even if exception occurs + app.Dispose (); // Ensure cleanup even if exception occurs } ``` @@ -561,44 +568,44 @@ finally ```csharp // RECOMMENDED (using statement): -using (var app = Application.Create().Init()) +using (IApplication app = Application.Create ().Init ()) { - app.Run(); - var result = app.GetResult(); + app.Run (); + MyResult? result = app.GetResult (); // app.Dispose() called automatically here } // ALTERNATIVE (manual disposal): -var app = Application.Create().Init(); -app.Run(); -var result = app.GetResult(); -app.Dispose(); // Must call explicitly +IApplication app = Application.Create ().Init (); +app.Run (); +MyResult? result = app.GetResult (); +app.Dispose (); // Must call explicitly // OLD (obsolete - do not use): -var result = app.Run().Shutdown() as MyResult; +object? result = app.Run ().Shutdown (); ``` ### Input Thread Lifecycle -When you call `Init()`, Terminal.Gui starts a dedicated input thread that continuously polls for console input. This thread must be stopped properly: +When calling `Init()`, Terminal.Gui starts a dedicated input thread that continuously polls for console input. This thread must be stopped properly: ```csharp -var app = Application.Create(); -app.Init("fake"); // Input thread starts here +IApplication app = Application.Create (); +app.Init ("fake"); // Input thread starts here // Input thread runs in background at ~50 polls/second (20ms throttle) -app.Dispose(); // Cancels input thread and waits for it to exit +app.Dispose (); // Cancels input thread and waits for it to exit ``` **Important for Tests**: Always dispose applications in tests to prevent thread leaks: ```csharp [Fact] -public void My_Test() +public void My_Test () { - using var app = Application.Create(); - app.Init("fake"); + using IApplication app = Application.Create (); + app.Init ("fake"); // Test code here @@ -612,22 +619,22 @@ The legacy static `Application` singleton can be re-initialized after disposal ( ```csharp // Test 1 -Application.Init(); -Application.Shutdown(); // Obsolete but still works for legacy singleton +Application.Init (); +Application.Shutdown (); // Obsolete but still works for legacy singleton // Test 2 - singleton resets and can be re-initialized -Application.Init(); // ✅ Works! -Application.Shutdown(); // Obsolete but still works for legacy singleton +Application.Init (); // ✅ Works! +Application.Shutdown (); // Obsolete but still works for legacy singleton ``` However, instance-based applications follow standard `IDisposable` semantics and cannot be reused after disposal: ```csharp -var app = Application.Create(); -app.Init(); -app.Dispose(); +IApplication app = Application.Create (); +app.Init (); +app.Dispose (); -app.Init(); // ❌ Throws ObjectDisposedException +app.Init (); // ❌ Throws ObjectDisposedException ``` ## Session Management @@ -637,21 +644,21 @@ app.Init(); // ❌ Throws ObjectDisposedException Applications manage sessions through `Begin()` and `End()`: ```csharp -using var app = Application.Create (); -app.Init(); +using IApplication app = Application.Create (); +app.Init (); -var window = new Window(); +Window window = new (); // Begin a new session - pushes to SessionStack -SessionToken? token = app.Begin(window); +SessionToken? token = app.Begin (window); // TopRunnable now points to this window -Debug.Assert(app.TopRunnable == window); +Debug.Assert (app.TopRunnable == window); // End the session - pops from SessionStack if (token != null) { - app.End(token); + app.End (token); } // TopRunnable restored to previous runnable (if any) @@ -662,25 +669,25 @@ if (token != null) Multiple sessions can run nested: ```csharp -using var app = Application.Create (); -app.Init(); +using IApplication app = Application.Create (); +app.Init (); // Session 1 -var main = new Window { Title = "Main" }; -var token1 = app.Begin(main); +Window main = new () { Title = "Main" }; +SessionToken? token1 = app.Begin (main); // app.TopRunnable == main, SessionStack.Count == 1 // Session 2 (nested) -var dialog = new Dialog { Title = "Dialog" }; -var token2 = app.Begin(dialog); +Dialog dialog = new () { Title = "Dialog" }; +SessionToken? token2 = app.Begin (dialog); // app.TopRunnable == dialog, SessionStack.Count == 2 // End dialog -app.End(token2); +app.End (token2); // app.TopRunnable == main, SessionStack.Count == 1 // End main -app.End(token1); +app.End (token1); // app.TopRunnable == null, SessionStack.Count == 0 ``` @@ -694,24 +701,24 @@ public class View /// /// Gets the driver for this view. /// - public IDriver? Driver => GetDriver(); + public IDriver? Driver => GetDriver (); /// /// Gets the driver, checking application context if needed. /// Override to customize driver resolution. /// - public virtual IDriver? GetDriver() => App?.Driver; + public virtual IDriver? GetDriver () => App?.Driver; } ``` **Usage:** ```csharp -public override void OnDrawContent(Rectangle viewport) +public override void OnDrawContent (Rectangle viewport) { // Use view's driver instead of Application.Driver - Driver?.Move(0, 0); - Driver?.AddStr("Hello"); + Driver?.Move (0, 0); + Driver?.AddStr ("Hello"); } ``` @@ -723,44 +730,44 @@ The instance-based architecture dramatically improves testability: ```csharp [Fact] -public void MyView_DisplaysCorrectly() +public void MyView_DisplaysCorrectly () { // Create mock application - var mockApp = new Mock(); - mockApp.Setup(a => a.Current).Returns(new Runnable()); + Mock mockApp = new (); + mockApp.Setup (a => a.TopRunnable).Returns (new Runnable ()); // Create view with mock app - var view = new MyView { App = mockApp.Object }; + MyView view = new () { App = mockApp.Object }; // Test without Application.Init()! - view.SetNeedsDraw(); - Assert.True(view.NeedsDraw); + view.SetNeedsDraw (); + Assert.True (view.NeedsDraw); // No Application.Shutdown() needed! } ``` -### Testing with Real ApplicationImpl +### Testing with Real Application ```csharp [Fact] -public void MyView_WorksWithRealApplication() +public void MyView_WorksWithRealApplication () { - using var app = Application.Create (); - app.Init("fake"); + using IApplication app = Application.Create (); + app.Init ("fake"); - var view = new MyView(); - var top = new Window(); - top.Add(view); + MyView view = new (); + Window top = new (); + top.Add (view); - app.Begin(top); + app.Begin (top); // View.App automatically set - Assert.NotNull(view.App); - Assert.Same(app, view.App); + Assert.NotNull (view.App); + Assert.Same (app, view.App); // Test view behavior - view.DoSomething(); + view.DoSomething (); } ``` @@ -770,9 +777,9 @@ public void MyView_WorksWithRealApplication() ```csharp ✅ GOOD: -public void Refresh() +public void Refresh () { - App?.TopRunnableView?.SetNeedsDraw(); + App?.TopRunnableView?.SetNeedsDraw (); } ``` @@ -780,9 +787,9 @@ public void Refresh() ```csharp ❌ AVOID: -public void Refresh() +public void Refresh () { - Application.TopRunnableView?.SetNeedsDraw(); // Obsolete! + Application.TopRunnableView?.SetNeedsDraw (); // Obsolete! } ``` @@ -792,7 +799,7 @@ public void Refresh() ✅ GOOD: public class Service { - public Service(IApplication app) { } + public Service (IApplication app) { } } ``` @@ -800,15 +807,15 @@ public class Service ```csharp ❌ AVOID (obsolete pattern): -public void Refresh() +public void Refresh () { - Application.TopRunnableView?.SetNeedsDraw(); // Obsolete static access + Application.TopRunnableView?.SetNeedsDraw (); // Obsolete static access } ✅ PREFERRED: -public void Refresh() +public void Refresh () { - App?.TopRunnableView?.SetNeedsDraw(); // Use View.App property + App?.TopRunnableView?.SetNeedsDraw (); // Use View.App property } ``` @@ -820,9 +827,9 @@ public class SpecialView : View { private IApplication? _customApp; - public override IApplication? GetApp() + public override IApplication? GetApp () { - return _customApp ?? base.GetApp(); + return _customApp ?? base.GetApp (); } } ``` @@ -835,15 +842,15 @@ The instance-based architecture enables multiple applications: ```csharp // Application 1 -using var app1 = Application.Create (); -app1.Init("windows"); -var top1 = new Window { Title = "App 1" }; +using IApplication app1 = Application.Create (); +app1.Init ("windows"); +Window top1 = new () { Title = "App 1" }; // ... configure top1 // Application 2 (different driver!) -using var app2 = Application.Create (); -app2.Init("unix"); -var top2 = new Window { Title = "App 2" }; +using IApplication app2 = Application.Create (); +app2.Init ("unix"); +Window top2 = new () { Title = "App 2" }; // ... configure top2 // Views in top1 use app1 @@ -857,14 +864,14 @@ Create views that work with any application: ```csharp public class UniversalView : View { - public void ShowMessage(string message) + public void ShowMessage (string message) { // Works regardless of which application context - var app = GetApp(); + IApplication? app = GetApp (); if (app != null) { - var msg = new MessageBox(message); - app.Begin(msg); + MessageBox msg = new (message); + app.Begin (msg); } } } diff --git a/docfx/docs/drivers.md b/docfx/docs/drivers.md index a7f523347..df57efcfd 100644 --- a/docfx/docs/drivers.md +++ b/docfx/docs/drivers.md @@ -97,7 +97,7 @@ The driver architecture employs a **multi-threaded design** for optimal responsi ``` ┌─────────────────────────────────────────────┐ -│ ApplicationImpl.Init() │ +│ IApplication.Init() │ │ Creates MainLoopCoordinator with │ │ ComponentFactory │ └────────────────┬────────────────────────────┘ @@ -128,7 +128,7 @@ This separation ensures that input is never lost and the UI remains responsive d When you call `Application.Init()`: -1. **ApplicationImpl.Init()** is invoked +1. **IApplication.Init()** is invoked 2. Creates a `MainLoopCoordinator` with the appropriate `ComponentFactory` 3. **MainLoopCoordinator.StartAsync()** begins: - Starts the input thread which creates `IInput` @@ -220,6 +220,5 @@ This ensures Terminal.Gui applications can be debugged directly in Visual Studio ## See Also - @Terminal.Gui.Drivers - API Reference -- @Terminal.Gui.App.Application - Application class -- @Terminal.Gui.App.ApplicationImpl - Application implementation +- @Terminal.Gui.App.IApplication - Application interface - @Terminal.Gui.App.MainLoopCoordinator`1 - Main loop coordination diff --git a/docfx/docs/keyboard.md b/docfx/docs/keyboard.md index 74f36ff8c..c68708b65 100644 --- a/docfx/docs/keyboard.md +++ b/docfx/docs/keyboard.md @@ -230,7 +230,7 @@ The @Terminal.Gui.App.Keyboard class implements @Terminal.Gui.App.IKeyboard and - **Events**: KeyDown, KeyUp events for application-level keyboard monitoring - **Command Implementations**: Handlers for Application-scoped commands (Quit, Suspend, Navigation, Refresh, Arrange) -The @Terminal.Gui.App.ApplicationImpl class creates and manages the @Terminal.Gui.App.IKeyboard instance, setting its `IApplication` property to `this` to provide the necessary @Terminal.Gui.App.IApplication reference. +The @Terminal.Gui.App.IApplication implementations create and manage the @Terminal.Gui.App.IKeyboard instance, setting its `IApplication` property to `this` to provide the necessary @Terminal.Gui.App.IApplication reference. ## Driver