Tig 2 周之前
父节点
当前提交
d9899dcf1e
共有 100 个文件被更改,包括 1998 次插入1062 次删除
  1. 74 28
      .github/workflows/unit-tests.yml
  2. 1 0
      .gitignore
  3. 2 2
      Examples/Example/Example.cs
  4. 2 2
      Examples/NativeAot/Program.cs
  5. 30 27
      Examples/RunnableWrapperExample/Program.cs
  6. 2 2
      Examples/SelfContained/Program.cs
  7. 3 3
      Examples/UICatalog/Scenario.cs
  8. 8 8
      Examples/UICatalog/Scenarios/Adornments.cs
  9. 1 1
      Examples/UICatalog/Scenarios/AnimationScenario/AnimationScenario.cs
  10. 5 5
      Examples/UICatalog/Scenarios/Bars.cs
  11. 3 3
      Examples/UICatalog/Scenarios/Buttons.cs
  12. 3 2
      Examples/UICatalog/Scenarios/ChineseUI.cs
  13. 3 3
      Examples/UICatalog/Scenarios/ConfigurationEditor.cs
  14. 5 5
      Examples/UICatalog/Scenarios/ContextMenus.cs
  15. 15 15
      Examples/UICatalog/Scenarios/CsvEditor.cs
  16. 1 1
      Examples/UICatalog/Scenarios/Dialogs.cs
  17. 5 5
      Examples/UICatalog/Scenarios/DynamicStatusBar.cs
  18. 11 10
      Examples/UICatalog/Scenarios/Editor.cs
  19. 1 1
      Examples/UICatalog/Scenarios/EditorsAndHelpers/DimEditor.cs
  20. 1 1
      Examples/UICatalog/Scenarios/EditorsAndHelpers/PosEditor.cs
  21. 5 5
      Examples/UICatalog/Scenarios/FileDialogExamples.cs
  22. 1 1
      Examples/UICatalog/Scenarios/Generic.cs
  23. 4 4
      Examples/UICatalog/Scenarios/HexEditor.cs
  24. 3 3
      Examples/UICatalog/Scenarios/Images.cs
  25. 1 1
      Examples/UICatalog/Scenarios/InteractiveTree.cs
  26. 4 4
      Examples/UICatalog/Scenarios/KeyBindings.cs
  27. 1 1
      Examples/UICatalog/Scenarios/ListColumns.cs
  28. 9 9
      Examples/UICatalog/Scenarios/MessageBoxes.cs
  29. 1 1
      Examples/UICatalog/Scenarios/MultiColouredTable.cs
  30. 2 2
      Examples/UICatalog/Scenarios/Navigation.cs
  31. 8 8
      Examples/UICatalog/Scenarios/Notepad.cs
  32. 3 3
      Examples/UICatalog/Scenarios/RunTExample.cs
  33. 3 3
      Examples/UICatalog/Scenarios/RuneWidthGreaterThanOne.cs
  34. 1 1
      Examples/UICatalog/Scenarios/Shortcuts.cs
  35. 4 4
      Examples/UICatalog/Scenarios/SingleBackgroundWorker.cs
  36. 7 7
      Examples/UICatalog/Scenarios/TableEditor.cs
  37. 2 2
      Examples/UICatalog/Scenarios/Transparent.cs
  38. 2 2
      Examples/UICatalog/Scenarios/ViewportSettings.cs
  39. 3 3
      Examples/UICatalog/Scenarios/WindowsAndFrameViews.cs
  40. 6 3
      Examples/UICatalog/Scenarios/WizardAsView.cs
  41. 269 266
      Examples/UICatalog/Scenarios/Wizards.cs
  42. 1 0
      Examples/UICatalog/UICatalogTop.cs
  43. 15 0
      Terminal.Gui/App/Application.Clipboard.cs
  44. 21 15
      Terminal.Gui/App/Application.Driver.cs
  45. 17 4
      Terminal.Gui/App/Application.Lifecycle.cs
  46. 12 2
      Terminal.Gui/App/Application.Mouse.cs
  47. 49 9
      Terminal.Gui/App/Application.Navigation.cs
  48. 25 5
      Terminal.Gui/App/Application.Run.cs
  49. 1 1
      Terminal.Gui/App/Application.TopRunnable.cs
  50. 103 49
      Terminal.Gui/App/ApplicationImpl.Lifecycle.cs
  51. 2 11
      Terminal.Gui/App/ApplicationImpl.Run.cs
  52. 136 12
      Terminal.Gui/App/ApplicationImpl.cs
  53. 16 0
      Terminal.Gui/App/ApplicationModelUsage.cs
  54. 1 1
      Terminal.Gui/App/ApplicationNavigation.cs
  55. 1 1
      Terminal.Gui/App/ApplicationRunnableExtensions.cs
  56. 32 0
      Terminal.Gui/App/Clipboard/Clipboard.cs
  57. 27 4
      Terminal.Gui/App/IApplication.cs
  58. 0 5
      Terminal.Gui/App/IterationEventArgs.cs
  59. 110 55
      Terminal.Gui/App/Keyboard/KeyboardImpl.cs
  60. 27 4
      Terminal.Gui/App/Mouse/MouseImpl.cs
  61. 2 2
      Terminal.Gui/App/Runnable/IRunnable.cs
  62. 80 4
      Terminal.Gui/Configuration/ConfigurationManager.cs
  63. 5 7
      Terminal.Gui/Configuration/SourcesManager.cs
  64. 4 4
      Terminal.Gui/Drivers/FakeDriver/FakeInputProcessor.cs
  65. 9 6
      Terminal.Gui/Drivers/IInputProcessor.cs
  66. 1 1
      Terminal.Gui/Drivers/InputProcessorImpl.cs
  67. 10 8
      Terminal.Gui/Drivers/OutputBase.cs
  68. 1 1
      Terminal.Gui/Drivers/WindowsDriver/WindowsInputProcessor.cs
  69. 3 2
      Terminal.Gui/Drivers/WindowsDriver/WindowsOutput.cs
  70. 6 3
      Terminal.Gui/FileServices/IFileOperations.cs
  71. 95 83
      Terminal.Gui/Input/InputBindings.cs
  72. 1 1
      Terminal.Gui/ViewBase/Runnable.cs
  73. 2 1
      Terminal.Gui/ViewBase/RunnableWrapper.cs
  74. 1 0
      Terminal.Gui/ViewBase/View.Diagnostics.cs
  75. 2 2
      Terminal.Gui/ViewBase/View.Drawing.Attribute.cs
  76. 13 3
      Terminal.Gui/Views/Button.cs
  77. 7 7
      Terminal.Gui/Views/CharMap/CharMap.cs
  78. 7 3
      Terminal.Gui/Views/CheckBox.cs
  79. 35 4
      Terminal.Gui/Views/CollectionNavigation/CollectionNavigator.cs
  80. 38 10
      Terminal.Gui/Views/CollectionNavigation/CollectionNavigatorBase.cs
  81. 2 2
      Terminal.Gui/Views/Color/ColorPicker.Prompt.cs
  82. 37 7
      Terminal.Gui/Views/Dialog.cs
  83. 9 9
      Terminal.Gui/Views/FileDialogs/DefaultFileOperations.cs
  84. 10 10
      Terminal.Gui/Views/FileDialogs/FileDialog.cs
  85. 9 5
      Terminal.Gui/Views/FrameView.cs
  86. 0 1
      Terminal.Gui/Views/GraphView/GraphView.cs
  87. 4 4
      Terminal.Gui/Views/Menu/MenuBar.cs
  88. 412 178
      Terminal.Gui/Views/MessageBox.cs
  89. 1 1
      Terminal.Gui/Views/Selectors/SelectorBase.cs
  90. 8 2
      Terminal.Gui/Views/StatusBar.cs
  91. 2 2
      Terminal.Gui/Views/TableView/TableView.cs
  92. 5 5
      Terminal.Gui/Views/TextInput/TextField.cs
  93. 4 4
      Terminal.Gui/Views/TextInput/TextView.cs
  94. 13 4
      Terminal.Gui/Views/Window.cs
  95. 1 1
      Terminal.Gui/Views/Wizard/Wizard.cs
  96. 12 11
      Tests/IntegrationTests/FluentTests/FileDialogFluentTests.cs
  97. 1 1
      Tests/IntegrationTests/FluentTests/GuiTestContextTests.cs
  98. 1 1
      Tests/StressTests/ScenariosStressTests.cs
  99. 1 6
      Tests/TerminalGuiFluentTesting/GuiTestContext.Input.cs
  100. 33 21
      Tests/TerminalGuiFluentTesting/GuiTestContext.cs

+ 74 - 28
.github/workflows/unit-tests.yml

@@ -120,7 +120,7 @@ jobs:
       matrix:
         os: [ ubuntu-latest, windows-latest, macos-latest ]
 
-    timeout-minutes: 15
+    timeout-minutes: 60
     steps:
 
     - name: Checkout code
@@ -154,35 +154,81 @@ jobs:
       shell: bash
       run: echo "VSTEST_DUMP_PATH=logs/UnitTestsParallelizable/${{ runner.os }}/" >> $GITHUB_ENV
 
-    - name: Run UnitTestsParallelizable
+    - name: Run UnitTestsParallelizable (10 iterations with varying parallelization)
       shell: bash
       run: |
-        if [ "${{ runner.os }}" == "Linux" ]; then
-          # Run with coverage on Linux only
-          dotnet test Tests/UnitTestsParallelizable \
-            --no-build \
-            --verbosity normal \
-            --collect:"XPlat Code Coverage" \
-            --settings Tests/UnitTests/runsettings.coverage.xml \
-            --diag:logs/UnitTestsParallelizable/${{ runner.os }}/logs.txt \
-            --blame \
-            --blame-crash \
-            --blame-hang \
-            --blame-hang-timeout 60s \
-            --blame-crash-collect-always
-        else
-          # Run without coverage on Windows/macOS for speed
-          dotnet test Tests/UnitTestsParallelizable \
-            --no-build \
-            --verbosity normal \
-            --settings Tests/UnitTestsParallelizable/runsettings.xml \
-            --diag:logs/UnitTestsParallelizable/${{ runner.os }}/logs.txt \
-            --blame \
-            --blame-crash \
-            --blame-hang \
-            --blame-hang-timeout 60s \
-            --blame-crash-collect-always
-        fi
+        # Run tests 10 times with different parallelization settings to expose concurrency issues
+        for RUN in {1..10}; do
+          echo "============================================"
+          echo "Starting test run $RUN of 10"
+          echo "============================================"
+          
+          # Use a combination of run number and timestamp to create different execution patterns
+          SEED=$((1000 + $RUN + $(date +%s) % 1000))
+          echo "Using randomization seed: $SEED"
+          
+          # Vary the xUnit parallelization based on run number to expose race conditions
+          # Runs 1-3: Default parallelization (2x CPU cores)
+          # Runs 4-6: Max parallelization (unlimited)
+          # Runs 7-9: Single threaded (1)
+          # Run 10: Random (1-4 threads)
+          if [ $RUN -le 3 ]; then
+            XUNIT_MAX_PARALLEL_THREADS="2x"
+            echo "Run $RUN: Using default parallelization (2x)"
+          elif [ $RUN -le 6 ]; then
+            XUNIT_MAX_PARALLEL_THREADS="unlimited"
+            echo "Run $RUN: Using maximum parallelization (unlimited)"
+          elif [ $RUN -le 9 ]; then
+            XUNIT_MAX_PARALLEL_THREADS="1"
+            echo "Run $RUN: Using single-threaded execution"
+          else
+            # Random parallelization based on seed
+            PROC_COUNT=$(( ($SEED % 4) + 1 ))
+            XUNIT_MAX_PARALLEL_THREADS="$PROC_COUNT"
+            echo "Run $RUN: Using random parallelization with $PROC_COUNT threads"
+          fi
+          
+          # Run tests with or without coverage based on OS and run number
+          if [ "${{ runner.os }}" == "Linux" ] && [ $RUN -eq 1 ]; then
+            echo "Run $RUN: Running with coverage collection"
+            dotnet test Tests/UnitTestsParallelizable \
+              --no-build \
+              --verbosity normal \
+              --collect:"XPlat Code Coverage" \
+              --settings Tests/UnitTests/runsettings.coverage.xml \
+              --diag:logs/UnitTestsParallelizable/${{ runner.os }}/run${RUN}-logs.txt \
+              --blame \
+              --blame-crash \
+              --blame-hang \
+              --blame-hang-timeout 60s \
+              --blame-crash-collect-always \
+              -- xUnit.MaxParallelThreads=${XUNIT_MAX_PARALLEL_THREADS}
+          else
+            dotnet test Tests/UnitTestsParallelizable \
+              --no-build \
+              --verbosity normal \
+              --settings Tests/UnitTestsParallelizable/runsettings.xml \
+              --diag:logs/UnitTestsParallelizable/${{ runner.os }}/run${RUN}-logs.txt \
+              --blame \
+              --blame-crash \
+              --blame-hang \
+              --blame-hang-timeout 60s \
+              --blame-crash-collect-always \
+              -- xUnit.MaxParallelThreads=${XUNIT_MAX_PARALLEL_THREADS}
+          fi
+          
+          if [ $? -ne 0 ]; then
+            echo "ERROR: Test run $RUN failed!"
+            exit 1
+          fi
+          
+          echo "Test run $RUN completed successfully"
+          echo ""
+        done
+        
+        echo "============================================"
+        echo "All 10 test runs completed successfully!"
+        echo "============================================"
 
     - name: Upload UnitTestsParallelizable Logs
       if: always()

+ 1 - 0
.gitignore

@@ -73,3 +73,4 @@ log.*
 !/Tests/coverage/.gitkeep   # keep folder in repo
 /Tests/report/
 *.cobertura.xml
+/docfx/docs/migratingfromv1.md

+ 2 - 2
Examples/Example/Example.cs

@@ -77,13 +77,13 @@ public class ExampleWindow : Window
                            {
                                if (userNameText.Text == "admin" && passwordText.Text == "password")
                                {
-                                   MessageBox.Query ("Logging In", "Login Successful", "Ok");
+                                   MessageBox.Query (App, "Logging In", "Login Successful", "Ok");
                                    UserName = userNameText.Text;
                                    Application.RequestStop ();
                                }
                                else
                                {
-                                   MessageBox.ErrorQuery ("Logging In", "Incorrect username or password", "Ok");
+                                   MessageBox.ErrorQuery (App, "Logging In", "Incorrect username or password", "Ok");
                                }
                                // When Accepting is handled, set e.Handled to true to prevent further processing.
                                e.Handled = true;

+ 2 - 2
Examples/NativeAot/Program.cs

@@ -101,13 +101,13 @@ public class ExampleWindow : Window
         {
             if (userNameText.Text == "admin" && passwordText.Text == "password")
             {
-                MessageBox.Query ("Logging In", "Login Successful", "Ok");
+                MessageBox.Query (App, "Logging In", "Login Successful", "Ok");
                 UserName = userNameText.Text;
                 Application.RequestStop ();
             }
             else
             {
-                MessageBox.ErrorQuery ("Logging In", "Incorrect username or password", "Ok");
+                MessageBox.ErrorQuery (App, "Logging In", "Incorrect username or password", "Ok");
             }
             // Anytime Accepting is handled, make sure to set e.Handled to true.
             e.Handled = true;

+ 30 - 27
Examples/RunnableWrapperExample/Program.cs

@@ -13,41 +13,42 @@ var textField = new TextField { Width = 40, Text = "Default text" };
 textField.Title = "Enter your name";
 textField.BorderStyle = LineStyle.Single;
 
-var textRunnable = textField.AsRunnable (tf => tf.Text);
+RunnableWrapper<TextField, string> textRunnable = textField.AsRunnable (tf => tf.Text);
 app.Run (textRunnable);
 
 if (textRunnable.Result is { } name)
 {
-    MessageBox.Query ("Result", $"You entered: {name}", "OK");
+    MessageBox.Query (app, "Result", $"You entered: {name}", "OK");
 }
 else
 {
-    MessageBox.Query ("Result", "Canceled", "OK");
+    MessageBox.Query (app, "Result", "Canceled", "OK");
 }
+
 textRunnable.Dispose ();
 
 // Example 2: Use IApplication.RunView() for one-liner
-var selectedColor = app.RunView (
-    new ColorPicker
-    {
-        Title = "Pick a Color",
-        BorderStyle = LineStyle.Single
-    },
-    cp => cp.SelectedColor);
+Color selectedColor = app.RunView (
+                                   new ColorPicker
+                                   {
+                                       Title = "Pick a Color",
+                                       BorderStyle = LineStyle.Single
+                                   },
+                                   cp => cp.SelectedColor);
 
-MessageBox.Query ("Result", $"Selected color: {selectedColor}", "OK");
+MessageBox.Query (app, "Result", $"Selected color: {selectedColor}", "OK");
 
 // Example 3: FlagSelector with typed enum result
-var flagSelector = new FlagSelector<SelectorStyles>
+FlagSelector<SelectorStyles> flagSelector = new()
 {
     Title = "Choose Styles",
     BorderStyle = LineStyle.Single
 };
 
-var flagsRunnable = flagSelector.AsRunnable (fs => fs.Value);
+RunnableWrapper<FlagSelector<SelectorStyles>, SelectorStyles?> flagsRunnable = flagSelector.AsRunnable (fs => fs.Value);
 app.Run (flagsRunnable);
 
-MessageBox.Query ("Result", $"Selected styles: {flagsRunnable.Result}", "OK");
+MessageBox.Query (app, "Result", $"Selected styles: {flagsRunnable.Result}", "OK");
 flagsRunnable.Dispose ();
 
 // Example 4: Any View without result extraction
@@ -58,26 +59,28 @@ var label = new Label
     Y = Pos.Center ()
 };
 
-var labelRunnable = label.AsRunnable ();
+RunnableWrapper<Label, object> labelRunnable = label.AsRunnable ();
 app.Run (labelRunnable);
 
 // Can still access the wrapped view
-MessageBox.Query ("Result", $"Label text was: {labelRunnable.WrappedView.Text}", "OK");
+MessageBox.Query (app, "Result", $"Label text was: {labelRunnable.WrappedView.Text}", "OK");
 labelRunnable.Dispose ();
 
 // Example 5: Complex custom View made runnable
-var formView = CreateCustomForm ();
-var formRunnable = formView.AsRunnable (ExtractFormData);
+View formView = CreateCustomForm ();
+RunnableWrapper<View, FormData> formRunnable = formView.AsRunnable (ExtractFormData);
 
 app.Run (formRunnable);
 
 if (formRunnable.Result is { } formData)
 {
     MessageBox.Query (
-        "Form Results",
-        $"Name: {formData.Name}\nAge: {formData.Age}\nAgreed: {formData.Agreed}",
-        "OK");
+                      app,
+                      "Form Results",
+                      $"Name: {formData.Name}\nAge: {formData.Age}\nAgreed: {formData.Agreed}",
+                      "OK");
 }
+
 formRunnable.Dispose ();
 
 app.Shutdown ();
@@ -126,10 +129,10 @@ View CreateCustomForm ()
     };
 
     okButton.Accepting += (s, e) =>
-    {
-        form.App?.RequestStop ();
-        e.Handled = true;
-    };
+                          {
+                              form.App?.RequestStop ();
+                              e.Handled = true;
+                          };
 
     form.Add (new Label { Text = "Name:", X = 2, Y = 1 });
     form.Add (nameField);
@@ -148,7 +151,7 @@ FormData ExtractFormData (View form)
     var ageField = form.SubViews.FirstOrDefault (v => v.Id == "ageField") as TextField;
     var agreeCheckbox = form.SubViews.FirstOrDefault (v => v.Id == "agreeCheckbox") as CheckBox;
 
-    return new FormData
+    return new()
     {
         Name = nameField?.Text ?? string.Empty,
         Age = int.TryParse (ageField?.Text, out int age) ? age : 0,
@@ -157,7 +160,7 @@ FormData ExtractFormData (View form)
 }
 
 // Result type for custom form
-record FormData
+internal record FormData
 {
     public string Name { get; init; } = string.Empty;
     public int Age { get; init; }

+ 2 - 2
Examples/SelfContained/Program.cs

@@ -100,13 +100,13 @@ public class ExampleWindow : Window
                            {
                                if (userNameText.Text == "admin" && passwordText.Text == "password")
                                {
-                                   MessageBox.Query ("Logging In", "Login Successful", "Ok");
+                                   MessageBox.Query (App, "Logging In", "Login Successful", "Ok");
                                    UserName = userNameText.Text;
                                    Application.RequestStop ();
                                }
                                else
                                {
-                                   MessageBox.ErrorQuery ("Logging In", "Incorrect username or password", "Ok");
+                                   MessageBox.ErrorQuery (App, "Logging In", "Incorrect username or password", "Ok");
                                }
                                // When Accepting is handled, set e.Handled to true to prevent further processing.
                                e.Handled = true;

+ 3 - 3
Examples/UICatalog/Scenario.cs

@@ -67,7 +67,7 @@ namespace UICatalog;
 ///         };
 /// 
 ///         var button = new Button { X = Pos.Center (), Y = Pos.Center (), Text = "Press me!" };
-///         button.Accept += (s, e) => MessageBox.ErrorQuery ("Error", "You pressed the button!", "Ok");
+///         button.Accept += (s, e) => MessageBox.ErrorQuery (App, "Error", "You pressed the button!", "Ok");
 ///         appWindow.Add (button);
 /// 
 ///         // Run - Start the application.
@@ -210,12 +210,12 @@ public class Scenario : IDisposable
         void OnClearedContents (object? sender, EventArgs args) => BenchmarkResults.ClearedContentCount++;
     }
 
-    private void OnApplicationOnIteration (object? s, IterationEventArgs a)
+    private void OnApplicationOnIteration (object? s, EventArgs<IApplication?> a)
     {
         BenchmarkResults.IterationCount++;
         if (BenchmarkResults.IterationCount > BENCHMARK_MAX_NATURAL_ITERATIONS + (_demoKeys!.Count * BENCHMARK_KEY_PACING))
         {
-            Application.RequestStop ();
+            a.Value?.RequestStop ();
         }
     }
 

+ 8 - 8
Examples/UICatalog/Scenarios/Adornments.cs

@@ -11,7 +11,7 @@ public class Adornments : Scenario
     {
         Application.Init ();
 
-        Window app = new ()
+        Window appWindow = new ()
         {
             Title = GetQuitKeyAndName (),
             BorderStyle = LineStyle.None
@@ -28,7 +28,7 @@ public class Adornments : Scenario
 
         editor.Border!.Thickness = new (1, 2, 1, 1);
 
-        app.Add (editor);
+        appWindow.Add (editor);
 
         var window = new Window
         {
@@ -38,7 +38,7 @@ public class Adornments : Scenario
             Width = Dim.Fill (Dim.Func (_ => editor.Frame.Width)),
             Height = Dim.Fill ()
         };
-        app.Add (window);
+        appWindow.Add (window);
 
         var tf1 = new TextField { Width = 10, Text = "TextField" };
         var color = new ColorPicker16 { Title = "BG", BoxHeight = 1, BoxWidth = 1, X = Pos.AnchorEnd () };
@@ -60,7 +60,7 @@ public class Adornments : Scenario
         var button = new Button { X = Pos.Center (), Y = Pos.Center (), Text = "Press me!" };
 
         button.Accepting += (s, e) =>
-                             MessageBox.Query (20, 7, "Hi", $"Am I a {window.GetType ().Name}?", "Yes", "No");
+                             MessageBox.Query (appWindow.App, 20, 7, "Hi", $"Am I a {window.GetType ().Name}?", "Yes", "No");
 
         var label = new TextView
         {
@@ -121,7 +121,7 @@ public class Adornments : Scenario
                                       Text = "text (Y = 1)",
                                       CanFocus = true
                                   };
-                                  textFieldInPadding.Accepting += (s, e) => MessageBox.Query (20, 7, "TextField", textFieldInPadding.Text, "Ok");
+                                  textFieldInPadding.Accepting += (s, e) => MessageBox.Query (appWindow.App, 20, 7, "TextField", textFieldInPadding.Text, "Ok");
                                   window.Padding.Add (textFieldInPadding);
 
                                   var btnButtonInPadding = new Button
@@ -132,7 +132,7 @@ public class Adornments : Scenario
                                       CanFocus = true,
                                       HighlightStates = MouseState.None,
                                   };
-                                  btnButtonInPadding.Accepting += (s, e) => MessageBox.Query (20, 7, "Hi", "Button in Padding Pressed!", "Ok");
+                                  btnButtonInPadding.Accepting += (s, e) => MessageBox.Query (appWindow.App, 20, 7, "Hi", "Button in Padding Pressed!", "Ok");
                                   btnButtonInPadding.BorderStyle = LineStyle.Dashed;
                                   btnButtonInPadding.Border!.Thickness = new (1, 1, 1, 1);
                                   window.Padding.Add (btnButtonInPadding);
@@ -155,8 +155,8 @@ public class Adornments : Scenario
         editor.AutoSelectSuperView = window;
         editor.AutoSelectAdornments = true;
 
-        Application.Run (app);
-        app.Dispose ();
+        Application.Run (appWindow);
+        appWindow.Dispose ();
 
         Application.Shutdown ();
     }

+ 1 - 1
Examples/UICatalog/Scenarios/AnimationScenario/AnimationScenario.cs

@@ -78,7 +78,7 @@ public class AnimationScenario : Scenario
         if (!f.Exists)
         {
             Debug.WriteLine ($"Could not find {f.FullName}");
-            MessageBox.ErrorQuery ("Could not find gif", $"Could not find\n{f.FullName}", "Ok");
+            MessageBox.ErrorQuery (_imageView?.App, "Could not find gif", $"Could not find\n{f.FullName}", "Ok");
 
             return;
         }

+ 5 - 5
Examples/UICatalog/Scenarios/Bars.cs

@@ -309,7 +309,7 @@ public class Bars : Scenario
     //                                                  new TimeSpan (0),
     //                                                  () =>
     //                                                  {
-    //                                                      MessageBox.Query ("File", "New");
+    //                                                      MessageBox.Query (App, "File", "New");
 
     //                                                      return false;
     //                                                  });
@@ -331,7 +331,7 @@ public class Bars : Scenario
     //                                               new TimeSpan (0),
     //                                               () =>
     //                                               {
-    //                                                   MessageBox.Query ("File", "Open");
+    //                                                   MessageBox.Query (App, "File", "Open");
 
     //                                                   return false;
     //                                               });
@@ -353,7 +353,7 @@ public class Bars : Scenario
     //                                               new TimeSpan (0),
     //                                               () =>
     //                                               {
-    //                                                   MessageBox.Query ("File", "Save");
+    //                                                   MessageBox.Query (App, "File", "Save");
 
     //                                                   return false;
     //                                               });
@@ -375,7 +375,7 @@ public class Bars : Scenario
     //                                                 new TimeSpan (0),
     //                                                 () =>
     //                                                 {
-    //                                                     MessageBox.Query ("File", "Save As");
+    //                                                     MessageBox.Query (App, "File", "Save As");
 
     //                                                     return false;
     //                                                 });
@@ -555,7 +555,7 @@ public class Bars : Scenario
 
         return;
 
-        void Button_Clicked (object sender, EventArgs e) { MessageBox.Query ("Hi", $"You clicked {sender}"); }
+        void Button_Clicked (object sender, EventArgs e) { MessageBox.Query ((sender as View)?.App, "Hi", $"You clicked {sender}"); }
 
     }
 

+ 3 - 3
Examples/UICatalog/Scenarios/Buttons.cs

@@ -59,7 +59,7 @@ public class Buttons : Scenario
 
                                     if (e.Handled)
                                     {
-                                        MessageBox.ErrorQuery ("Error", "This button is no longer the Quit button; the Swap Default button is.", "_Ok");
+                                        MessageBox.ErrorQuery ((s as View)?.App, "Error", "This button is no longer the Quit button; the Swap Default button is.", "_Ok");
                                     }
                                 };
         main.Add (swapButton);
@@ -69,7 +69,7 @@ public class Buttons : Scenario
             button.Accepting += (s, e) =>
                              {
                                  string btnText = button.Text;
-                                 MessageBox.Query ("Message", $"Did you click {txt}?", "Yes", "No");
+                                 MessageBox.Query ((s as View)?.App, "Message", $"Did you click {txt}?", "Yes", "No");
                                  e.Handled = true;
                              };
         }
@@ -112,7 +112,7 @@ public class Buttons : Scenario
                  );
         button.Accepting += (s, e) =>
                          {
-                             MessageBox.Query ("Message", "Question?", "Yes", "No");
+                             MessageBox.Query ((s as View)?.App, "Message", "Question?", "Yes", "No");
                              e.Handled = true;
                          };
 

+ 3 - 2
Examples/UICatalog/Scenarios/ChineseUI.cs

@@ -32,8 +32,9 @@ public class ChineseUI : Scenario
 
         btn.Accepting += (s, e) =>
                       {
-                          int result = MessageBox.Query (
-                                                         "Confirm",
+                          int? result = MessageBox.Query (
+                                                          (s as View)?.App,
+                                                          "Confirm",
                                                          "Are you sure you want to quit ui?",
                                                          0,
                                                          "Yes",

+ 3 - 3
Examples/UICatalog/Scenarios/ConfigurationEditor.cs

@@ -153,9 +153,9 @@ public class ConfigurationEditor : Scenario
                 continue;
             }
 
-            int result = MessageBox.Query (
+            int? result = MessageBox.Query (editor?.App,
                                            "Save Changes",
-                                           $"Save changes to {editor.FileInfo!.Name}",
+                                           $"Save changes to {editor?.FileInfo!.Name}",
                                            "_Yes",
                                            "_No",
                                            "_Cancel"
@@ -164,7 +164,7 @@ public class ConfigurationEditor : Scenario
             switch (result)
             {
                 case 0:
-                    editor.Save ();
+                    editor?.Save ();
 
                     break;
 

+ 5 - 5
Examples/UICatalog/Scenarios/ContextMenus.cs

@@ -49,7 +49,7 @@ public class ContextMenus : Scenario
             var text = "Context Menu";
             var width = 20;
 
-            CreateWinContextMenu ();
+            CreateWinContextMenu (ApplicationImpl.Instance);
 
             var label = new Label
             {
@@ -108,7 +108,7 @@ public class ContextMenus : Scenario
         }
     }
 
-    private void CreateWinContextMenu ()
+    private void CreateWinContextMenu (IApplication? app)
     {
         _winContextMenu = new (
                                [
@@ -122,7 +122,7 @@ public class ContextMenus : Scenario
                                    {
                                        Title = "_Configuration...",
                                        HelpText = "Show configuration",
-                                       Action = () => MessageBox.Query (
+                                       Action = () => MessageBox.Query (app,
                                                                         50,
                                                                         10,
                                                                         "Configuration",
@@ -140,7 +140,7 @@ public class ContextMenus : Scenario
                                                               Title = "_Setup...",
                                                               HelpText = "Perform setup",
                                                               Action = () => MessageBox
-                                                                           .Query (
+                                                                           .Query (app,
                                                                                    50,
                                                                                    10,
                                                                                    "Setup",
@@ -154,7 +154,7 @@ public class ContextMenus : Scenario
                                                               Title = "_Maintenance...",
                                                               HelpText = "Maintenance mode",
                                                               Action = () => MessageBox
-                                                                           .Query (
+                                                                           .Query (app,
                                                                                    50,
                                                                                    10,
                                                                                    "Maintenance",

+ 15 - 15
Examples/UICatalog/Scenarios/CsvEditor.cs

@@ -215,7 +215,7 @@ public class CsvEditor : Scenario
                                       _tableView.Table.Columns
                                      );
 
-            int result = MessageBox.Query (
+            int? result = MessageBox.Query (ApplicationImpl.Instance,
                                            "Column Type",
                                            "Pick a data type for the column",
                                            "Date",
@@ -225,7 +225,7 @@ public class CsvEditor : Scenario
                                            "Cancel"
                                           );
 
-            if (result <= -1 || result >= 4)
+            if (result is null || result >= 4)
             {
                 return;
             }
@@ -308,7 +308,7 @@ public class CsvEditor : Scenario
 
         if (_tableView.SelectedColumn == -1)
         {
-            MessageBox.ErrorQuery ("No Column", "No column selected", "Ok");
+            MessageBox.ErrorQuery (ApplicationImpl.Instance, "No Column", "No column selected", "Ok");
 
             return;
         }
@@ -320,7 +320,7 @@ public class CsvEditor : Scenario
         }
         catch (Exception ex)
         {
-            MessageBox.ErrorQuery ("Could not remove column", ex.Message, "Ok");
+            MessageBox.ErrorQuery (ApplicationImpl.Instance, "Could not remove column", ex.Message, "Ok");
         }
     }
 
@@ -342,7 +342,7 @@ public class CsvEditor : Scenario
             }
             catch (Exception ex)
             {
-                MessageBox.ErrorQuery (60, 20, "Failed to set text", ex.Message, "Ok");
+                MessageBox.ErrorQuery (ApplicationImpl.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 ("No Column", "No column selected", "Ok");
+            MessageBox.ErrorQuery (ApplicationImpl.Instance, "No Column", "No column selected", "Ok");
 
             return;
         }
@@ -413,7 +413,7 @@ public class CsvEditor : Scenario
         }
         catch (Exception ex)
         {
-            MessageBox.ErrorQuery ("Error moving column", ex.Message, "Ok");
+            MessageBox.ErrorQuery (ApplicationImpl.Instance, "Error moving column", ex.Message, "Ok");
         }
     }
 
@@ -426,7 +426,7 @@ public class CsvEditor : Scenario
 
         if (_tableView.SelectedRow == -1)
         {
-            MessageBox.ErrorQuery ("No Rows", "No row selected", "Ok");
+            MessageBox.ErrorQuery (ApplicationImpl.Instance, "No Rows", "No row selected", "Ok");
 
             return;
         }
@@ -446,7 +446,7 @@ public class CsvEditor : Scenario
                     return;
                 }
 
-                object?[] arrayItems = currentRow.ItemArray;
+                object? [] arrayItems = currentRow.ItemArray;
                 _currentTable.Rows.Remove (currentRow);
 
                 // Removing and Inserting the same DataRow seems to result in it loosing its values so we have to create a new instance
@@ -462,7 +462,7 @@ public class CsvEditor : Scenario
         }
         catch (Exception ex)
         {
-            MessageBox.ErrorQuery ("Error moving column", ex.Message, "Ok");
+            MessageBox.ErrorQuery (ApplicationImpl.Instance, "Error moving column", ex.Message, "Ok");
         }
     }
 
@@ -470,7 +470,7 @@ public class CsvEditor : Scenario
     {
         if (_tableView?.Table is null)
         {
-            MessageBox.ErrorQuery ("No Table Loaded", "No table has currently be opened", "Ok");
+            MessageBox.ErrorQuery (ApplicationImpl.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 (
+            MessageBox.ErrorQuery (ApplicationImpl.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 ("No file loaded", "No file is currently loaded", "Ok");
+            MessageBox.ErrorQuery (ApplicationImpl.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 (
+            MessageBox.ErrorQuery (ApplicationImpl.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 ("No Column", "No column selected", "Ok");
+            MessageBox.ErrorQuery (ApplicationImpl.Instance, "No Column", "No column selected", "Ok");
 
             return;
         }

+ 1 - 1
Examples/UICatalog/Scenarios/Dialogs.cs

@@ -266,7 +266,7 @@ public class Dialogs : Scenario
             {
                 Title = titleEdit.Text,
                 Text = "Dialog Text",
-                ButtonAlignment = (Alignment)Enum.Parse (typeof (Alignment), alignmentGroup.Labels! [(int)alignmentGroup.Value!.Value] [1..]),
+                ButtonAlignment = (Alignment)Enum.Parse (typeof (Alignment), alignmentGroup.Labels! [(int)alignmentGroup.Value!.Value] [0..]),
 
                 Buttons = buttons.ToArray ()
             };

+ 5 - 5
Examples/UICatalog/Scenarios/DynamicStatusBar.cs

@@ -79,7 +79,7 @@ public class DynamicStatusBar : Scenario
             }
             catch (Exception ex)
             {
-                MessageBox.ErrorQuery ("Binding Error", $"Binding failed: {ex}.", "Ok");
+                MessageBox.ErrorQuery (ApplicationImpl.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 (item.Title, item.Action, "Ok"); }
+        public Action CreateAction (DynamicStatusItem item) { return () => MessageBox.ErrorQuery (ApplicationImpl.Instance, item.Title, item.Action, "Ok"); }
 
         public void EditStatusItem (Shortcut statusItem)
         {
@@ -184,7 +184,7 @@ public class DynamicStatusBar : Scenario
                               {
                                   if (string.IsNullOrEmpty (TextTitle.Text))
                                   {
-                                      MessageBox.ErrorQuery ("Invalid title", "Must enter a valid title!.", "Ok");
+                                      MessageBox.ErrorQuery (App, "Invalid title", "Must enter a valid title!.", "Ok");
                                   }
                                   else
                                   {
@@ -200,7 +200,7 @@ public class DynamicStatusBar : Scenario
                                       TextTitle.Text = string.Empty;
                                       Application.RequestStop ();
                                   };
-            var dialog = new Dialog { Title = "Enter the menu details.", Buttons = [btnOk, btnCancel], Height = Dim.Auto (DimAutoStyle.Content, 17, Application.Screen.Height) };
+            var dialog = new Dialog { Title = "Enter the menu details.", Buttons = [btnOk, btnCancel], Height = Dim.Auto (DimAutoStyle.Content, 17, App?.Screen.Height) };
 
             Width = Dim.Fill ();
             Height = Dim.Fill () - 2;
@@ -382,7 +382,7 @@ public class DynamicStatusBar : Scenario
                               {
                                   if (string.IsNullOrEmpty (frmStatusBarDetails.TextTitle.Text) && _currentEditStatusItem != null)
                                   {
-                                      MessageBox.ErrorQuery ("Invalid title", "Must enter a valid title!.", "Ok");
+                                      MessageBox.ErrorQuery (App, "Invalid title", "Must enter a valid title!.", "Ok");
                                   }
                                   else if (_currentEditStatusItem != null)
                                   {

+ 11 - 10
Examples/UICatalog/Scenarios/Editor.cs

@@ -156,7 +156,7 @@ public class Editor : Scenario
                                        new (Key.F2, "Open", Open),
                                        new (Key.F3, "Save", () => Save ()),
                                        new (Key.F4, "Save As", () => SaveAs ()),
-                                       new (Key.Empty, $"OS Clipboard IsSupported : {Clipboard.IsSupported}", null),
+                                       new (Key.Empty, $"OS Clipboard IsSupported : {Application.Clipboard!.IsSupported}", null),
                                        siCursorPosition
                                    ]
                                   )
@@ -193,7 +193,8 @@ public class Editor : Scenario
 
         Debug.Assert (_textView.IsDirty);
 
-        int r = MessageBox.ErrorQuery (
+        int? r = MessageBox.ErrorQuery (
+                                       ApplicationImpl.Instance,
                                        "Save File",
                                        $"Do you want save changes in {_appWindow.Title}?",
                                        "Yes",
@@ -228,7 +229,7 @@ public class Editor : Scenario
         }
         catch (Exception ex)
         {
-            MessageBox.ErrorQuery ("Error", ex.Message, "Ok");
+            MessageBox.ErrorQuery (ApplicationImpl.Instance, "Error", ex.Message, "Ok");
         }
     }
 
@@ -307,11 +308,11 @@ public class Editor : Scenario
 
         if (!found)
         {
-            MessageBox.Query ("Find", $"The following specified text was not found: '{_textToFind}'", "Ok");
+            MessageBox.Query (ApplicationImpl.Instance, "Find", $"The following specified text was not found: '{_textToFind}'", "Ok");
         }
         else if (gaveFullTurn)
         {
-            MessageBox.Query (
+            MessageBox.Query (ApplicationImpl.Instance,
                               "Find",
                               $"No more occurrences were found for the following specified text: '{_textToFind}'",
                               "Ok"
@@ -887,7 +888,7 @@ public class Editor : Scenario
 
         if (_textView.ReplaceAllText (_textToFind, _matchCase, _matchWholeWord, _textToReplace))
         {
-            MessageBox.Query (
+            MessageBox.Query (ApplicationImpl.Instance,
                               "Replace All",
                               $"All occurrences were replaced for the following specified text: '{_textToReplace}'",
                               "Ok"
@@ -895,7 +896,7 @@ public class Editor : Scenario
         }
         else
         {
-            MessageBox.Query (
+            MessageBox.Query (ApplicationImpl.Instance,
                               "Replace All",
                               $"None of the following specified text was found: '{_textToFind}'",
                               "Ok"
@@ -1147,7 +1148,7 @@ public class Editor : Scenario
         {
             if (File.Exists (path))
             {
-                if (MessageBox.Query (
+                if (MessageBox.Query (ApplicationImpl.Instance,
                                       "Save File",
                                       "File already exists. Overwrite any way?",
                                       "No",
@@ -1186,11 +1187,11 @@ public class Editor : Scenario
             _originalText = Encoding.Unicode.GetBytes (_textView.Text);
             _saved = true;
             _textView.ClearHistoryChanges ();
-            MessageBox.Query ("Save File", "File was successfully saved.", "Ok");
+            MessageBox.Query (ApplicationImpl.Instance, "Save File", "File was successfully saved.", "Ok");
         }
         catch (Exception ex)
         {
-            MessageBox.ErrorQuery ("Error", ex.Message, "Ok");
+            MessageBox.ErrorQuery (ApplicationImpl.Instance, "Error", ex.Message, "Ok");
 
             return false;
         }

+ 1 - 1
Examples/UICatalog/Scenarios/EditorsAndHelpers/DimEditor.cs

@@ -157,7 +157,7 @@ public class DimEditor : EditorBase
         }
         catch (Exception e)
         {
-            MessageBox.ErrorQuery ("Exception", e.Message, "Ok");
+            MessageBox.ErrorQuery (App, "Exception", e.Message, "Ok");
         }
     }
 }

+ 1 - 1
Examples/UICatalog/Scenarios/EditorsAndHelpers/PosEditor.cs

@@ -160,7 +160,7 @@ public class PosEditor : EditorBase
         }
         catch (Exception e)
         {
-            MessageBox.ErrorQuery ("Exception", e.Message, "Ok");
+            MessageBox.ErrorQuery (App, "Exception", e.Message, "Ok");
         }
     }
 }

+ 5 - 5
Examples/UICatalog/Scenarios/FileDialogExamples.cs

@@ -133,7 +133,7 @@ public class FileDialogExamples : Scenario
                              }
                              catch (Exception ex)
                              {
-                                 MessageBox.ErrorQuery ("Error", ex.ToString (), "_Ok");
+                                 MessageBox.ErrorQuery (ApplicationImpl.Instance, "Error", ex.ToString (), "_Ok");
                              }
                              finally
                              {
@@ -153,7 +153,7 @@ public class FileDialogExamples : Scenario
         {
             if (File.Exists (e.Dialog.Path))
             {
-                int result = MessageBox.Query ("Overwrite?", "File already exists", "_Yes", "_No");
+                int? result = MessageBox.Query (ApplicationImpl.Instance, "Overwrite?", "File already exists", "_Yes", "_No");
                 e.Cancel = result == 1;
             }
         }
@@ -248,7 +248,7 @@ public class FileDialogExamples : Scenario
 
             if (canceled)
             {
-                MessageBox.Query (
+                MessageBox.Query (ApplicationImpl.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 (
+                MessageBox.Query (ApplicationImpl.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 (
+                MessageBox.Query (ApplicationImpl.Instance,
                                   "Chosen!",
                                   "You chose:" + Environment.NewLine + path,
                                   "Ok"

+ 1 - 1
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 ("Error", "You pressed the button!", "_Ok");
+                                MessageBox.ErrorQuery (ApplicationImpl.Instance, "Error", "You pressed the button!", "_Ok");
                             };
 
         appWindow.Add (button);

+ 4 - 4
Examples/UICatalog/Scenarios/HexEditor.cs

@@ -181,7 +181,7 @@ public class HexEditor : Scenario
         }
     }
 
-    private void Copy () { MessageBox.ErrorQuery ("Not Implemented", "Functionality not yet implemented.", "Ok"); }
+    private void Copy () { MessageBox.ErrorQuery (ApplicationImpl.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 ("Not Implemented", "Functionality not yet implemented.", "Ok"); }
+    private void Cut () { MessageBox.ErrorQuery (ApplicationImpl.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 (
+            if (MessageBox.ErrorQuery (ApplicationImpl.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 ("Not Implemented", "Functionality not yet implemented.", "_Ok"); }
+    private void Paste () { MessageBox.ErrorQuery (ApplicationImpl.Instance, "Not Implemented", "Functionality not yet implemented.", "_Ok"); }
     private void Quit () { Application.RequestStop (); }
 
     private void Save ()

+ 3 - 3
Examples/UICatalog/Scenarios/Images.cs

@@ -183,7 +183,7 @@ public class Images : Scenario
 
         if (!_sixelSupportResult.SupportsTransparency)
         {
-            if (MessageBox.Query (
+            if (MessageBox.Query (ApplicationImpl.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 ("Could not open file", ex.Message, "Ok");
+            MessageBox.ErrorQuery (ApplicationImpl.Instance, "Could not open file", ex.Message, "Ok");
 
             return;
         }
@@ -492,7 +492,7 @@ public class Images : Scenario
     {
         if (_imageView.FullResImage == null)
         {
-            MessageBox.Query ("No Image Loaded", "You must first open an image.  Use the 'Open Image' button above.", "Ok");
+            MessageBox.Query (ApplicationImpl.Instance, "No Image Loaded", "You must first open an image.  Use the 'Open Image' button above.", "Ok");
 
             return;
         }

+ 1 - 1
Examples/UICatalog/Scenarios/InteractiveTree.cs

@@ -173,7 +173,7 @@ public class InteractiveTree : Scenario
 
                 if (parent is null)
                 {
-                    MessageBox.ErrorQuery (
+                    MessageBox.ErrorQuery (ApplicationImpl.Instance,
                                            "Could not delete",
                                            $"Parent of '{toDelete}' was unexpectedly null",
                                            "Ok"

+ 4 - 4
Examples/UICatalog/Scenarios/KeyBindings.cs

@@ -164,17 +164,17 @@ public class KeyBindingsDemo : View
 
         AddCommand (Command.Save, ctx =>
                                  {
-                                     MessageBox.Query ($"{ctx.Command}", $"Ctx: {ctx}", buttons: "Ok");
+                                     MessageBox.Query (ApplicationImpl.Instance, $"{ctx.Command}", $"Ctx: {ctx}", buttons: "Ok");
                                      return true;
                                  });
         AddCommand (Command.New, ctx =>
                                 {
-                                    MessageBox.Query ($"{ctx.Command}", $"Ctx: {ctx}", buttons: "Ok");
+                                    MessageBox.Query (ApplicationImpl.Instance, $"{ctx.Command}", $"Ctx: {ctx}", buttons: "Ok");
                                     return true;
                                 });
         AddCommand (Command.HotKey, ctx =>
         {
-            MessageBox.Query ($"{ctx.Command}", $"Ctx: {ctx}\nCommand: {ctx.Command}", buttons: "Ok");
+            MessageBox.Query (ApplicationImpl.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 ($"{keyCommandContext.Binding}", $"Key: {keyCommandContext.Binding.Key}\nCommand: {ctx.Command}", buttons: "Ok");
+                                             MessageBox.Query (ApplicationImpl.Instance, $"{keyCommandContext.Binding}", $"Key: {keyCommandContext.Binding.Key}\nCommand: {ctx.Command}", buttons: "Ok");
                                              Application.RequestStop ();
                                              return true;
                                          });

+ 1 - 1
Examples/UICatalog/Scenarios/ListColumns.cs

@@ -336,7 +336,7 @@ public class ListColumns : Scenario
             }
             catch (Exception ex)
             {
-                MessageBox.ErrorQuery (60, 20, "Failed to set", ex.Message, "Ok");
+                MessageBox.ErrorQuery (ApplicationImpl.Instance, 60, 20, "Failed to set", ex.Message, "Ok");
             }
         }
     }

+ 9 - 9
Examples/UICatalog/Scenarios/MessageBoxes.cs

@@ -251,7 +251,7 @@ public class MessageBoxes : Scenario
                                                {
                                                    buttonPressedLabel.Text =
                                                        $"{MessageBox.Query (
-                                                                             width,
+                                                                            ApplicationImpl.Instance, width,
                                                                              height,
                                                                              titleEdit.Text,
                                                                              messageEdit.Text,
@@ -263,14 +263,14 @@ public class MessageBoxes : Scenario
                                                else
                                                {
                                                    buttonPressedLabel.Text =
-                                                       $"{MessageBox.ErrorQuery (
-                                                                                  width,
-                                                                                  height,
-                                                                                  titleEdit.Text,
-                                                                                  messageEdit.Text,
-                                                                                  defaultButton,
-                                                                                  ckbWrapMessage.CheckedState == CheckState.Checked,
-                                                                                  btns.ToArray ()
+                                                       $"{MessageBox.ErrorQuery (ApplicationImpl.Instance,
+                                                                                 width,
+                                                                                 height,
+                                                                                 titleEdit.Text,
+                                                                                 messageEdit.Text,
+                                                                                 defaultButton,
+                                                                                 ckbWrapMessage.CheckedState == CheckState.Checked,
+                                                                                 btns.ToArray ()
                                                                                  )}";
                                                }
                                            }

+ 1 - 1
Examples/UICatalog/Scenarios/MultiColouredTable.cs

@@ -99,7 +99,7 @@ public class MultiColouredTable : Scenario
             }
             catch (Exception ex)
             {
-                MessageBox.ErrorQuery (60, 20, "Failed to set text", ex.Message, "Ok");
+                MessageBox.ErrorQuery (ApplicationImpl.Instance, 60, 20, "Failed to set text", ex.Message, "Ok");
             }
 
             _tableView.Update ();

+ 2 - 2
Examples/UICatalog/Scenarios/Navigation.cs

@@ -59,7 +59,7 @@ public class Navigation : Scenario
             Y = 0,
             Title = $"TopButton _{GetNextHotKey ()}"
         };
-        button.Accepting += (sender, args) => MessageBox.Query ("hi", button.Title, "_Ok");
+        button.Accepting += (sender, args) => MessageBox.Query (ApplicationImpl.Instance, "hi", button.Title, "_Ok");
 
         testFrame.Add (button);
 
@@ -210,7 +210,7 @@ public class Navigation : Scenario
 
         return;
 
-        void OnApplicationIteration (object sender, IterationEventArgs args)
+        void OnApplicationIteration (object sender, EventArgs<IApplication> args)
         {
             if (progressBar.Fraction == 1.0)
             {

+ 8 - 8
Examples/UICatalog/Scenarios/Notepad.cs

@@ -71,7 +71,7 @@ public class Notepad : Scenario
                                        new MenuItem
                                        {
                                            Title = "_About",
-                                           Action = () => MessageBox.Query ("Notepad", "About Notepad...", "Ok")
+                                           Action = () => MessageBox.Query (ApplicationImpl.Instance,  "Notepad", "About Notepad...", "Ok")
                                        }
                                    ]
                                   )
@@ -193,15 +193,15 @@ public class Notepad : Scenario
 
         if (tab.UnsavedChanges)
         {
-            int result = MessageBox.Query (
-                                           "Save Changes",
-                                           $"Save changes to {tab.Text.TrimEnd ('*')}",
-                                           "Yes",
-                                           "No",
-                                           "Cancel"
+            int? result = MessageBox.Query (ApplicationImpl.Instance,
+                                            "Save Changes",
+                                            $"Save changes to {tab.Text.TrimEnd ('*')}",
+                                            "Yes",
+                                            "No",
+                                            "Cancel"
                                           );
 
-            if (result == -1 || result == 2)
+            if (result is null || result == 2)
             {
                 // user cancelled
                 return;

+ 3 - 3
Examples/UICatalog/Scenarios/RunTExample.cs

@@ -63,12 +63,12 @@ public class RunTExample : Scenario
                                {
                                    if (_usernameText.Text == "admin" && passwordText.Text == "password")
                                    {
-                                       MessageBox.Query ("Login Successful", $"Username: {_usernameText.Text}", "Ok");
-                                       Application.RequestStop ();
+                                       MessageBox.Query (App, "Login Successful", $"Username: {_usernameText.Text}", "Ok");
+                                       App?.RequestStop ();
                                    }
                                    else
                                    {
-                                       MessageBox.ErrorQuery (
+                                       MessageBox.ErrorQuery (App,
                                                               "Error Logging In",
                                                               "Incorrect username or password (hint: admin/password)",
                                                               "Ok"

+ 3 - 3
Examples/UICatalog/Scenarios/RuneWidthGreaterThanOne.cs

@@ -166,7 +166,7 @@ public class RuneWidthGreaterThanOne : Scenario
     {
         if (_text is { })
         {
-            MessageBox.Query ("Say Hello 你", $"Hello {_text.Text}", "Ok");
+            MessageBox.Query (ApplicationImpl.Instance, "Say Hello 你", $"Hello {_text.Text}", "Ok");
         }
     }
 
@@ -197,7 +197,7 @@ public class RuneWidthGreaterThanOne : Scenario
     {
         if (_text is { })
         {
-            MessageBox.Query ("Say Hello", $"Hello {_text.Text}", "Ok");
+            MessageBox.Query (ApplicationImpl.Instance, "Say Hello", $"Hello {_text.Text}", "Ok");
         }
     }
 
@@ -252,7 +252,7 @@ public class RuneWidthGreaterThanOne : Scenario
     {
         if (_text is { })
         {
-            MessageBox.Query ("こんにちはと言う", $"こんにちは {_text.Text}", "Ok");
+            MessageBox.Query (ApplicationImpl.Instance, "こんにちはと言う", $"こんにちは {_text.Text}", "Ok");
         }
     }
 

+ 1 - 1
Examples/UICatalog/Scenarios/Shortcuts.cs

@@ -566,6 +566,6 @@ public class Shortcuts : Scenario
     {
         e.Handled = true;
         var view = sender as View;
-        MessageBox.Query ("Hi", $"You clicked {view?.Text}", "_Ok");
+        MessageBox.Query ((sender as View)?.App, "Hi", $"You clicked {view?.Text}", "_Ok");
     }
 }

+ 4 - 4
Examples/UICatalog/Scenarios/SingleBackgroundWorker.cs

@@ -224,7 +224,7 @@ public class SingleBackgroundWorker : Scenario
 
             bool Close ()
             {
-                int n = MessageBox.Query (
+                int? n = MessageBox.Query (App,
                                           50,
                                           7,
                                           "Close Window.",
@@ -251,7 +251,7 @@ public class SingleBackgroundWorker : Scenario
                                                         {
                                                             if (Close ())
                                                             {
-                                                                Application.RequestStop ();
+                                                                App?.RequestStop ();
                                                             }
                                                         }
                                            }
@@ -270,7 +270,7 @@ public class SingleBackgroundWorker : Scenario
                                                 {
                                                     if (Close ())
                                                     {
-                                                        Application.RequestStop ();
+                                                        App?.RequestStop ();
                                                     }
                                                 }
                                                )
@@ -304,7 +304,7 @@ public class SingleBackgroundWorker : Scenario
         {
             if (_top is { })
             {
-                Application.Run (_top);
+                App?.Run (_top);
                 _top.Dispose ();
                 _top = null;
             }

+ 7 - 7
Examples/UICatalog/Scenarios/TableEditor.cs

@@ -1026,7 +1026,7 @@ public class TableEditor : Scenario
             }
             catch (Exception ex)
             {
-                MessageBox.ErrorQuery (60, 20, "Failed to set text", ex.Message, "Ok");
+                MessageBox.ErrorQuery ((sender as View)?.App, 60, 20, "Failed to set text", ex.Message, "Ok");
             }
 
             _tableView!.Update ();
@@ -1165,7 +1165,7 @@ public class TableEditor : Scenario
         }
         catch (Exception e)
         {
-            MessageBox.ErrorQuery ("Could not find local drives", e.Message, "Ok");
+            MessageBox.ErrorQuery (_tableView?.App, "Could not find local drives", e.Message, "Ok");
         }
 
         _tableView!.Table = source;
@@ -1199,10 +1199,10 @@ public class TableEditor : Scenario
         ok.Accepting += (s, e) =>
                         {
                             accepted = true;
-                            Application.RequestStop ();
+                            (s as View)?.App?.RequestStop ();
                         };
         var cancel = new Button { Text = "Cancel" };
-        cancel.Accepting += (s, e) => { Application.RequestStop (); };
+        cancel.Accepting += (s, e) => { (s as View)?.App?.RequestStop (); };
 
         var d = new Dialog
         {
@@ -1218,7 +1218,7 @@ public class TableEditor : Scenario
         d.Add (lbl, tf);
         tf.SetFocus ();
 
-        Application.Run (d);
+        _tableView.App?.Run (d);
         d.Dispose ();
 
         if (accepted)
@@ -1229,7 +1229,7 @@ public class TableEditor : Scenario
             }
             catch (Exception ex)
             {
-                MessageBox.ErrorQuery (60, 20, "Failed to set", ex.Message, "Ok");
+                MessageBox.ErrorQuery (_tableView.App, 60, 20, "Failed to set", ex.Message, "Ok");
             }
 
             _tableView!.Update ();
@@ -1512,7 +1512,7 @@ public class TableEditor : Scenario
                                                                              _checkedFileSystemInfos!.Contains,
                                                                              CheckOrUncheckFile
                                                                             )
-                { UseRadioButtons = radio };
+            { UseRadioButtons = radio };
         }
         else
         {

+ 2 - 2
Examples/UICatalog/Scenarios/Transparent.cs

@@ -46,7 +46,7 @@ public sealed class Transparent : Scenario
         };
         appButton.Accepting += (sender, args) =>
                                {
-                                   MessageBox.Query ("AppButton", "Transparency is cool!", "_Ok");
+                                   MessageBox.Query ((sender as View)?.App, "AppButton", "Transparency is cool!", "_Ok");
                                    args.Handled = true;
                                };
         appWindow.Add (appButton);
@@ -106,7 +106,7 @@ public sealed class Transparent : Scenario
             };
             button.Accepting += (sender, args) =>
                                 {
-                                    MessageBox.Query ("Clicked!", "Button in Transparent View", "_Ok");
+                                    MessageBox.Query (App, "Clicked!", "Button in Transparent View", "_Ok");
                                     args.Handled = true;
                                 };
             //button.Visible = false;

+ 2 - 2
Examples/UICatalog/Scenarios/ViewportSettings.cs

@@ -169,13 +169,13 @@ public class ViewportSettings : Scenario
         };
 
         charMap.Accepting += (s, e) =>
-                              MessageBox.Query (20, 7, "Hi", $"Am I a {view.GetType ().Name}?", "Yes", "No");
+                              MessageBox.Query ((s as View)?.App, 20, 7, "Hi", $"Am I a {view.GetType ().Name}?", "Yes", "No");
 
         var buttonAnchored = new Button
         {
             X = Pos.AnchorEnd () - 10, Y = Pos.AnchorEnd () - 4, Text = "Bottom Rig_ht"
         };
-        buttonAnchored.Accepting += (sender, args) => MessageBox.Query ("Hi", $"You pressed {((Button)sender)?.Text}", "_Ok");
+        buttonAnchored.Accepting += (sender, args) => MessageBox.Query ((sender as View)?.App, "Hi", $"You pressed {((Button)sender)?.Text}", "_Ok");
 
         view.Margin!.Data = "Margin";
         view.Margin!.Thickness = new (0);

+ 3 - 3
Examples/UICatalog/Scenarios/WindowsAndFrameViews.cs

@@ -16,9 +16,9 @@ public class WindowsAndFrameViews : Scenario
             Title = GetQuitKeyAndName ()
         };
 
-        static int About ()
+        static int? About ()
         {
-            return MessageBox.Query (
+            return MessageBox.Query (ApplicationImpl.Instance,
                                      "About UI Catalog",
                                      "UI Catalog is a comprehensive sample library for Terminal.Gui",
                                      "Ok"
@@ -99,7 +99,7 @@ public class WindowsAndFrameViews : Scenario
             };
 
             pressMeButton.Accepting += (s, e) =>
-                                        MessageBox.ErrorQuery (loopWin.Title, "Neat?", "Yes", "No");
+                                        MessageBox.ErrorQuery ((s as View)?.App, loopWin.Title, "Neat?", "Yes", "No");
             loopWin.Add (pressMeButton);
 
             var subWin = new Window

+ 6 - 3
Examples/UICatalog/Scenarios/WizardAsView.cs

@@ -21,6 +21,7 @@ public class WizardAsView : Scenario
                                        {
                                            Title = "_Restart Configuration...",
                                            Action = () => MessageBox.Query (
+                                                                            ApplicationImpl.Instance,
                                                                             "Wizard",
                                                                             "Are you sure you want to reset the Wizard and start over?",
                                                                             "Ok",
@@ -31,6 +32,7 @@ public class WizardAsView : Scenario
                                        {
                                            Title = "Re_boot Server...",
                                            Action = () => MessageBox.Query (
+                                                                            ApplicationImpl.Instance,
                                                                             "Wizard",
                                                                             "Are you sure you want to reboot the server start over?",
                                                                             "Ok",
@@ -41,6 +43,7 @@ public class WizardAsView : Scenario
                                        {
                                            Title = "_Shutdown Server...",
                                            Action = () => MessageBox.Query (
+                                                                            ApplicationImpl.Instance,
                                                                             "Wizard",
                                                                             "Are you sure you want to cancel setup and shutdown?",
                                                                             "Ok",
@@ -80,13 +83,13 @@ public class WizardAsView : Scenario
         wizard.Finished += (s, args) =>
                            {
                                //args.Cancel = true;
-                               MessageBox.Query ("Setup Wizard", "Finished", "Ok");
+                               MessageBox.Query ((s as View)?.App, "Setup Wizard", "Finished", "Ok");
                                Application.RequestStop ();
                            };
 
         wizard.Cancelled += (s, args) =>
                             {
-                                int btn = MessageBox.Query ("Setup Wizard", "Are you sure you want to cancel?", "Yes", "No");
+                                int? btn = MessageBox.Query ((s as View)?.App, "Setup Wizard", "Are you sure you want to cancel?", "Yes", "No");
                                 args.Cancel = btn == 1;
 
                                 if (btn == 0)
@@ -123,7 +126,7 @@ public class WizardAsView : Scenario
                             {
                                 secondStep.Title = "2nd Step";
 
-                                MessageBox.Query (
+                                MessageBox.Query ((s as View)?.App,
                                                   "Wizard Scenario",
                                                   "This Wizard Step's title was changed to '2nd Step'",
                                                   "Ok"

+ 269 - 266
Examples/UICatalog/Scenarios/Wizards.cs

@@ -1,13 +1,9 @@
-using System;
-using System.Linq;
-
-namespace UICatalog.Scenarios;
+namespace UICatalog.Scenarios;
 
 [ScenarioMetadata ("Wizards", "Demonstrates the Wizard class")]
 [ScenarioCategory ("Dialogs")]
 [ScenarioCategory ("Wizards")]
 [ScenarioCategory ("Runnable")]
-
 public class Wizards : Scenario
 {
     public override void Main ()
@@ -108,267 +104,277 @@ public class Wizards : Scenario
         };
 
         showWizardButton.Accepting += (s, e) =>
-                                   {
-                                       try
-                                       {
-                                           var width = 0;
-                                           int.TryParse (widthEdit.Text, out width);
-                                           var height = 0;
-                                           int.TryParse (heightEdit.Text, out height);
-
-                                           if (width < 1 || height < 1)
-                                           {
-                                               MessageBox.ErrorQuery (
-                                                                      "Nope",
-                                                                      "Height and width must be greater than 0 (much bigger)",
-                                                                      "Ok"
-                                                                     );
-
-                                               return;
-                                           }
-
-                                           actionLabel.Text = string.Empty;
-
-                                           var wizard = new Wizard { Title = titleEdit.Text, Width = width, Height = height };
-
-                                           wizard.MovingBack += (s, args) =>
-                                                                {
-                                                                    //args.Cancel = true;
-                                                                    actionLabel.Text = "Moving Back";
-                                                                };
-
-                                           wizard.MovingNext += (s, args) =>
-                                                                {
-                                                                    //args.Cancel = true;
-                                                                    actionLabel.Text = "Moving Next";
-                                                                };
-
-                                           wizard.Finished += (s, args) =>
-                                                              {
-                                                                  //args.Cancel = true;
-                                                                  actionLabel.Text = "Finished";
-                                                              };
-
-                                           wizard.Cancelled += (s, args) =>
-                                                               {
-                                                                   //args.Cancel = true;
-                                                                   actionLabel.Text = "Cancelled";
-                                                               };
-
-                                           // Add 1st step
-                                           var firstStep = new WizardStep { Title = "End User License Agreement" };
-                                           firstStep.NextButtonText = "Accept!";
-
-                                           firstStep.HelpText =
-                                               "This is the End User License Agreement.\n\n\n\n\n\nThis is a test of the emergency broadcast system. This is a test of the emergency broadcast system.\nThis is a test of the emergency broadcast system.\n\n\nThis is a test of the emergency broadcast system.\n\nThis is a test of the emergency broadcast system.\n\n\n\nThe end of the EULA.";
-
-                                           OptionSelector optionSelector = new ()
-                                           {
-                                               Labels = ["_One", "_Two", "_3"]
-                                           };
-                                           firstStep.Add (optionSelector);
-
-                                           wizard.AddStep (firstStep);
-
-                                           // Add 2nd step
-                                           var secondStep = new WizardStep { Title = "Second Step" };
-                                           wizard.AddStep (secondStep);
-
-                                           secondStep.HelpText =
-                                               "This is the help text for the Second Step.\n\nPress the button to change the Title.\n\nIf First Name is empty the step will prevent moving to the next step.";
-
-                                           var buttonLbl = new Label { Text = "Second Step Button: ", X = 1, Y = 1 };
-
-                                           var button = new Button
-                                           {
-                                               Text = "Press Me to Rename Step", X = Pos.Right (buttonLbl), Y = Pos.Top (buttonLbl)
-                                           };
-
-                                           OptionSelector optionSelecor2 = new ()
-                                           {
-                                               Labels = ["_A", "_B", "_C"],
-                                               Orientation = Orientation.Horizontal
-                                           };
-                                           secondStep.Add (optionSelecor2);
-
-                                           button.Accepting += (s, e) =>
-                                                            {
-                                                                secondStep.Title = "2nd Step";
-
-                                                                MessageBox.Query (
-                                                                                  "Wizard Scenario",
-                                                                                  "This Wizard Step's title was changed to '2nd Step'"
-                                                                                 );
-                                                            };
-                                           secondStep.Add (buttonLbl, button);
-                                           var lbl = new Label { Text = "First Name: ", X = 1, Y = Pos.Bottom (buttonLbl) };
-
-                                           var firstNameField =
-                                               new TextField { Text = "Number", Width = 30, X = Pos.Right (lbl), Y = Pos.Top (lbl) };
-                                           secondStep.Add (lbl, firstNameField);
-                                           lbl = new () { Text = "Last Name:  ", X = 1, Y = Pos.Bottom (lbl) };
-                                           var lastNameField = new TextField { Text = "Six", Width = 30, X = Pos.Right (lbl), Y = Pos.Top (lbl) };
-                                           secondStep.Add (lbl, lastNameField);
-
-                                           var thirdStepEnabledCeckBox = new CheckBox
-                                           {
-                                               Text = "Enable Step _3",
-                                               CheckedState = CheckState.UnChecked,
-                                               X = Pos.Left (lastNameField),
-                                               Y = Pos.Bottom (lastNameField)
-                                           };
-                                           secondStep.Add (thirdStepEnabledCeckBox);
-
-                                           // Add a frame 
-                                           var frame = new FrameView
-                                           {
-                                               X = 0,
-                                               Y = Pos.Bottom (thirdStepEnabledCeckBox) + 2,
-                                               Width = Dim.Fill (),
-                                               Height = 4,
-                                               Title = "A Broken Frame (by Depeche Mode)",
-                                               TabStop = TabBehavior.NoStop
-                                           };
-                                           frame.Add (new TextField { Text = "This is a TextField inside of the frame." });
-                                           secondStep.Add (frame);
-
-                                           wizard.StepChanging += (s, args) =>
+                                      {
+                                          try
+                                          {
+                                              var width = 0;
+                                              int.TryParse (widthEdit.Text, out width);
+                                              var height = 0;
+                                              int.TryParse (heightEdit.Text, out height);
+
+                                              if (width < 1 || height < 1)
+                                              {
+                                                  MessageBox.ErrorQuery (
+                                                                         (s as View)?.App,
+                                                                         "Nope",
+                                                                         "Height and width must be greater than 0 (much bigger)",
+                                                                         "Ok"
+                                                                        );
+
+                                                  return;
+                                              }
+
+                                              actionLabel.Text = string.Empty;
+
+                                              var wizard = new Wizard { Title = titleEdit.Text, Width = width, Height = height };
+
+                                              wizard.MovingBack += (s, args) =>
+                                                                   {
+                                                                       //args.Cancel = true;
+                                                                       actionLabel.Text = "Moving Back";
+                                                                   };
+
+                                              wizard.MovingNext += (s, args) =>
+                                                                   {
+                                                                       //args.Cancel = true;
+                                                                       actionLabel.Text = "Moving Next";
+                                                                   };
+
+                                              wizard.Finished += (s, args) =>
+                                                                 {
+                                                                     //args.Cancel = true;
+                                                                     actionLabel.Text = "Finished";
+                                                                 };
+
+                                              wizard.Cancelled += (s, args) =>
                                                                   {
-                                                                      if (args.OldStep == secondStep && string.IsNullOrEmpty (firstNameField.Text))
-                                                                      {
-                                                                          args.Cancel = true;
-
-                                                                          int btn = MessageBox.ErrorQuery (
-                                                                               "Second Step",
-                                                                               "You must enter a First Name to continue",
-                                                                               "Ok"
-                                                                              );
-                                                                      }
+                                                                      //args.Cancel = true;
+                                                                      actionLabel.Text = "Cancelled";
                                                                   };
 
-                                           // Add 3rd (optional) step
-                                           var thirdStep = new WizardStep { Title = "Third Step (Optional)" };
-                                           wizard.AddStep (thirdStep);
-
-                                           thirdStep.HelpText =
-                                               "This is step is optional (WizardStep.Enabled = false). Enable it with the checkbox in Step 2.";
-                                           var step3Label = new Label { Text = "This step is optional.", X = 0, Y = 0 };
-                                           thirdStep.Add (step3Label);
-                                           var progLbl = new Label { Text = "Third Step ProgressBar: ", X = 1, Y = 10 };
-
-                                           var progressBar = new ProgressBar
-                                           {
-                                               X = Pos.Right (progLbl), Y = Pos.Top (progLbl), Width = 40, Fraction = 0.42F
-                                           };
-                                           thirdStep.Add (progLbl, progressBar);
-                                           thirdStep.Enabled = thirdStepEnabledCeckBox.CheckedState == CheckState.Checked;
-                                           thirdStepEnabledCeckBox.CheckedStateChanged += (s, e) => { thirdStep.Enabled = thirdStepEnabledCeckBox.CheckedState == CheckState.Checked; };
-
-                                           // Add 4th step
-                                           var fourthStep = new WizardStep { Title = "Step Four" };
-                                           wizard.AddStep (fourthStep);
-
-                                           var someText = new TextView
-                                           {
-                                               Text =
-                                                   "This step (Step Four) shows how to show/hide the Help pane. The step contains this TextView (but it's hard to tell it's a TextView because of Issue #1800).",
-                                               X = 0,
-                                               Y = 0,
-                                               Width = Dim.Fill (),
-                                               WordWrap = true,
-                                               AllowsTab = false,
-                                               SchemeName = "Base"
-                                           };
-
-                                           someText.Height = Dim.Fill (
-                                                                       Dim.Func (
-                                                                                 v => someText.SuperView is { IsInitialized: true }
-                                                                                          ? someText.SuperView.SubViews
-                                                                                                    .First (view => view.Y.Has<PosAnchorEnd> (out _))
-                                                                                                    .Frame.Height
-                                                                                          : 1));
-                                           var help = "This is helpful.";
-                                           fourthStep.Add (someText);
-
-                                           var hideHelpBtn = new Button
-                                           {
-                                               Text = "Press me to show/hide help",
-                                               X = Pos.Center (),
-                                               Y = Pos.AnchorEnd ()
-                                           };
-
-                                           hideHelpBtn.Accepting += (s, e) =>
-                                                                 {
-                                                                     if (fourthStep.HelpText.Length > 0)
-                                                                     {
-                                                                         fourthStep.HelpText = string.Empty;
-                                                                     }
-                                                                     else
+                                              // Add 1st step
+                                              var firstStep = new WizardStep { Title = "End User License Agreement" };
+                                              firstStep.NextButtonText = "Accept!";
+
+                                              firstStep.HelpText =
+                                                  "This is the End User License Agreement.\n\n\n\n\n\nThis is a test of the emergency broadcast system. This is a test of the emergency broadcast system.\nThis is a test of the emergency broadcast system.\n\n\nThis is a test of the emergency broadcast system.\n\nThis is a test of the emergency broadcast system.\n\n\n\nThe end of the EULA.";
+
+                                              OptionSelector optionSelector = new ()
+                                              {
+                                                  Labels = ["_One", "_Two", "_3"]
+                                              };
+                                              firstStep.Add (optionSelector);
+
+                                              wizard.AddStep (firstStep);
+
+                                              // Add 2nd step
+                                              var secondStep = new WizardStep { Title = "Second Step" };
+                                              wizard.AddStep (secondStep);
+
+                                              secondStep.HelpText =
+                                                  "This is the help text for the Second Step.\n\nPress the button to change the Title.\n\nIf First Name is empty the step will prevent moving to the next step.";
+
+                                              var buttonLbl = new Label { Text = "Second Step Button: ", X = 1, Y = 1 };
+
+                                              var button = new Button
+                                              {
+                                                  Text = "Press Me to Rename Step", X = Pos.Right (buttonLbl), Y = Pos.Top (buttonLbl)
+                                              };
+
+                                              OptionSelector optionSelecor2 = new ()
+                                              {
+                                                  Labels = ["_A", "_B", "_C"],
+                                                  Orientation = Orientation.Horizontal
+                                              };
+                                              secondStep.Add (optionSelecor2);
+
+                                              button.Accepting += (s, e) =>
+                                                                  {
+                                                                      secondStep.Title = "2nd Step";
+
+                                                                      MessageBox.Query (
+                                                                                        (s as View)?.App,
+                                                                                        "Wizard Scenario",
+                                                                                        "This Wizard Step's title was changed to '2nd Step'"
+                                                                                       );
+                                                                  };
+                                              secondStep.Add (buttonLbl, button);
+                                              var lbl = new Label { Text = "First Name: ", X = 1, Y = Pos.Bottom (buttonLbl) };
+
+                                              var firstNameField =
+                                                  new TextField { Text = "Number", Width = 30, X = Pos.Right (lbl), Y = Pos.Top (lbl) };
+                                              secondStep.Add (lbl, firstNameField);
+                                              lbl = new () { Text = "Last Name:  ", X = 1, Y = Pos.Bottom (lbl) };
+                                              var lastNameField = new TextField { Text = "Six", Width = 30, X = Pos.Right (lbl), Y = Pos.Top (lbl) };
+                                              secondStep.Add (lbl, lastNameField);
+
+                                              var thirdStepEnabledCeckBox = new CheckBox
+                                              {
+                                                  Text = "Enable Step _3",
+                                                  CheckedState = CheckState.UnChecked,
+                                                  X = Pos.Left (lastNameField),
+                                                  Y = Pos.Bottom (lastNameField)
+                                              };
+                                              secondStep.Add (thirdStepEnabledCeckBox);
+
+                                              // Add a frame 
+                                              var frame = new FrameView
+                                              {
+                                                  X = 0,
+                                                  Y = Pos.Bottom (thirdStepEnabledCeckBox) + 2,
+                                                  Width = Dim.Fill (),
+                                                  Height = 4,
+                                                  Title = "A Broken Frame (by Depeche Mode)",
+                                                  TabStop = TabBehavior.NoStop
+                                              };
+                                              frame.Add (new TextField { Text = "This is a TextField inside of the frame." });
+                                              secondStep.Add (frame);
+
+                                              wizard.StepChanging += (s, args) =>
                                                                      {
-                                                                         fourthStep.HelpText = help;
-                                                                     }
-                                                                 };
-                                           fourthStep.Add (hideHelpBtn);
-                                           fourthStep.NextButtonText = "_Go To Last Step";
-                                           //var scrollBar = new ScrollBarView (someText, true);
-
-                                           //scrollBar.ChangedPosition += (s, e) =>
-                                           //                             {
-                                           //                                 someText.TopRow = scrollBar.Position;
-
-                                           //                                 if (someText.TopRow != scrollBar.Position)
-                                           //                                 {
-                                           //                                     scrollBar.Position = someText.TopRow;
-                                           //                                 }
-
-                                           //                                 someText.SetNeedsDraw ();
-                                           //                             };
-
-                                           //someText.DrawingContent += (s, e) =>
-                                           //                        {
-                                           //                            scrollBar.Size = someText.Lines;
-                                           //                            scrollBar.Position = someText.TopRow;
-
-                                           //                            if (scrollBar.OtherScrollBarView != null)
-                                           //                            {
-                                           //                                scrollBar.OtherScrollBarView.Size = someText.Maxlength;
-                                           //                                scrollBar.OtherScrollBarView.Position = someText.LeftColumn;
-                                           //                            }
-                                           //                        };
-                                           //fourthStep.Add (scrollBar);
-
-                                           // Add last step
-                                           var lastStep = new WizardStep { Title = "The last step" };
-                                           wizard.AddStep (lastStep);
-
-                                           lastStep.HelpText =
-                                               "The wizard is complete!\n\nPress the Finish button to continue.\n\nPressing ESC will cancel the wizard.";
-
-                                           var finalFinalStepEnabledCeckBox =
-                                               new CheckBox { Text = "Enable _Final Final Step", CheckedState = CheckState.UnChecked, X = 0, Y = 1 };
-                                           lastStep.Add (finalFinalStepEnabledCeckBox);
-
-                                           // Add an optional FINAL last step
-                                           var finalFinalStep = new WizardStep { Title = "The VERY last step" };
-                                           wizard.AddStep (finalFinalStep);
-
-                                           finalFinalStep.HelpText =
-                                               "This step only shows if it was enabled on the other last step.";
-                                           finalFinalStep.Enabled = thirdStepEnabledCeckBox.CheckedState == CheckState.Checked;
-
-                                           finalFinalStepEnabledCeckBox.CheckedStateChanged += (s, e) =>
-                                                                                   {
-                                                                                       finalFinalStep.Enabled = finalFinalStepEnabledCeckBox.CheckedState == CheckState.Checked;
-                                                                                   };
-
-                                           Application.Run (wizard);
-                                           wizard.Dispose ();
-                                       }
-                                       catch (FormatException)
-                                       {
-                                           actionLabel.Text = "Invalid Options";
-                                       }
-                                   };
+                                                                         if (args.OldStep == secondStep && string.IsNullOrEmpty (firstNameField.Text))
+                                                                         {
+                                                                             args.Cancel = true;
+
+                                                                             int? btn = MessageBox.ErrorQuery (
+                                                                                  (s as View)?.App,
+                                                                                  "Second Step",
+                                                                                  "You must enter a First Name to continue",
+                                                                                  "Ok"
+                                                                                 );
+                                                                         }
+                                                                     };
+
+                                              // Add 3rd (optional) step
+                                              var thirdStep = new WizardStep { Title = "Third Step (Optional)" };
+                                              wizard.AddStep (thirdStep);
+
+                                              thirdStep.HelpText =
+                                                  "This is step is optional (WizardStep.Enabled = false). Enable it with the checkbox in Step 2.";
+                                              var step3Label = new Label { Text = "This step is optional.", X = 0, Y = 0 };
+                                              thirdStep.Add (step3Label);
+                                              var progLbl = new Label { Text = "Third Step ProgressBar: ", X = 1, Y = 10 };
+
+                                              var progressBar = new ProgressBar
+                                              {
+                                                  X = Pos.Right (progLbl), Y = Pos.Top (progLbl), Width = 40, Fraction = 0.42F
+                                              };
+                                              thirdStep.Add (progLbl, progressBar);
+                                              thirdStep.Enabled = thirdStepEnabledCeckBox.CheckedState == CheckState.Checked;
+
+                                              thirdStepEnabledCeckBox.CheckedStateChanged += (s, e) =>
+                                                                                             {
+                                                                                                 thirdStep.Enabled =
+                                                                                                     thirdStepEnabledCeckBox.CheckedState == CheckState.Checked;
+                                                                                             };
+
+                                              // Add 4th step
+                                              var fourthStep = new WizardStep { Title = "Step Four" };
+                                              wizard.AddStep (fourthStep);
+
+                                              var someText = new TextView
+                                              {
+                                                  Text =
+                                                      "This step (Step Four) shows how to show/hide the Help pane. The step contains this TextView (but it's hard to tell it's a TextView because of Issue #1800).",
+                                                  X = 0,
+                                                  Y = 0,
+                                                  Width = Dim.Fill (),
+                                                  WordWrap = true,
+                                                  AllowsTab = false,
+                                                  SchemeName = "Base"
+                                              };
+
+                                              someText.Height = Dim.Fill (
+                                                                          Dim.Func (v => someText.SuperView is { IsInitialized: true }
+                                                                                             ? someText.SuperView.SubViews
+                                                                                                       .First (view => view.Y.Has<PosAnchorEnd> (out _))
+                                                                                                       .Frame.Height
+                                                                                             : 1));
+                                              var help = "This is helpful.";
+                                              fourthStep.Add (someText);
+
+                                              var hideHelpBtn = new Button
+                                              {
+                                                  Text = "Press me to show/hide help",
+                                                  X = Pos.Center (),
+                                                  Y = Pos.AnchorEnd ()
+                                              };
+
+                                              hideHelpBtn.Accepting += (s, e) =>
+                                                                       {
+                                                                           if (fourthStep.HelpText.Length > 0)
+                                                                           {
+                                                                               fourthStep.HelpText = string.Empty;
+                                                                           }
+                                                                           else
+                                                                           {
+                                                                               fourthStep.HelpText = help;
+                                                                           }
+                                                                       };
+                                              fourthStep.Add (hideHelpBtn);
+                                              fourthStep.NextButtonText = "_Go To Last Step";
+
+                                              //var scrollBar = new ScrollBarView (someText, true);
+
+                                              //scrollBar.ChangedPosition += (s, e) =>
+                                              //                             {
+                                              //                                 someText.TopRow = scrollBar.Position;
+
+                                              //                                 if (someText.TopRow != scrollBar.Position)
+                                              //                                 {
+                                              //                                     scrollBar.Position = someText.TopRow;
+                                              //                                 }
+
+                                              //                                 someText.SetNeedsDraw ();
+                                              //                             };
+
+                                              //someText.DrawingContent += (s, e) =>
+                                              //                        {
+                                              //                            scrollBar.Size = someText.Lines;
+                                              //                            scrollBar.Position = someText.TopRow;
+
+                                              //                            if (scrollBar.OtherScrollBarView != null)
+                                              //                            {
+                                              //                                scrollBar.OtherScrollBarView.Size = someText.Maxlength;
+                                              //                                scrollBar.OtherScrollBarView.Position = someText.LeftColumn;
+                                              //                            }
+                                              //                        };
+                                              //fourthStep.Add (scrollBar);
+
+                                              // Add last step
+                                              var lastStep = new WizardStep { Title = "The last step" };
+                                              wizard.AddStep (lastStep);
+
+                                              lastStep.HelpText =
+                                                  "The wizard is complete!\n\nPress the Finish button to continue.\n\nPressing ESC will cancel the wizard.";
+
+                                              var finalFinalStepEnabledCeckBox =
+                                                  new CheckBox { Text = "Enable _Final Final Step", CheckedState = CheckState.UnChecked, X = 0, Y = 1 };
+                                              lastStep.Add (finalFinalStepEnabledCeckBox);
+
+                                              // Add an optional FINAL last step
+                                              var finalFinalStep = new WizardStep { Title = "The VERY last step" };
+                                              wizard.AddStep (finalFinalStep);
+
+                                              finalFinalStep.HelpText =
+                                                  "This step only shows if it was enabled on the other last step.";
+                                              finalFinalStep.Enabled = thirdStepEnabledCeckBox.CheckedState == CheckState.Checked;
+
+                                              finalFinalStepEnabledCeckBox.CheckedStateChanged += (s, e) =>
+                                                                                                  {
+                                                                                                      finalFinalStep.Enabled =
+                                                                                                          finalFinalStepEnabledCeckBox.CheckedState
+                                                                                                          == CheckState.Checked;
+                                                                                                  };
+
+                                              Application.Run (wizard);
+                                              wizard.Dispose ();
+                                          }
+                                          catch (FormatException)
+                                          {
+                                              actionLabel.Text = "Invalid Options";
+                                          }
+                                      };
         win.Add (showWizardButton);
 
         Application.Run (win);
@@ -376,8 +382,5 @@ public class Wizards : Scenario
         Application.Shutdown ();
     }
 
-    private void Wizard_StepChanged (object sender, StepChangeEventArgs e)
-    {
-        throw new NotImplementedException ();
-    }
+    private void Wizard_StepChanged (object sender, StepChangeEventArgs e) { throw new NotImplementedException (); }
 }

+ 1 - 0
Examples/UICatalog/UICatalogTop.cs

@@ -144,6 +144,7 @@ public class UICatalogTop : Toplevel
                                                               "_About...",
                                                               "About UI Catalog",
                                                               () => MessageBox.Query (
+                                                                                      App,
                                                                                       "",
                                                                                       GetAboutBoxMessage (),
                                                                                       wrapMessage: false,

+ 15 - 0
Terminal.Gui/App/Application.Clipboard.cs

@@ -0,0 +1,15 @@
+namespace Terminal.Gui.App;
+
+public static partial class Application // Clipboard handling
+{
+    /// <summary>
+    ///     Gets the clipboard for the application.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         Provides access to the OS clipboard through the driver.
+    ///     </para>
+    /// </remarks>
+    [Obsolete ("The legacy static Application object is going away. Use IApplication.Clipboard instead.")]
+    public static IClipboard? Clipboard => ApplicationImpl.Instance.Clipboard;
+}

+ 21 - 15
Terminal.Gui/App/Application.Driver.cs

@@ -13,38 +13,44 @@ public static partial class Application // Driver abstractions
         internal set => ApplicationImpl.Instance.Driver = value;
     }
 
+    private static bool _force16Colors = false; // Resources/config.json overrides
+
     /// <inheritdoc cref="IApplication.Force16Colors"/>
     [ConfigurationProperty (Scope = typeof (SettingsScope))]
     [Obsolete ("The legacy static Application object is going away.")]
     public static bool Force16Colors
     {
-        get => ApplicationImpl.Instance.Force16Colors;
-        set => ApplicationImpl.Instance.Force16Colors = value;
+        get => _force16Colors;
+        set
+        {
+            bool oldValue = _force16Colors;
+            _force16Colors = value;
+            Force16ColorsChanged?.Invoke (null, new ValueChangedEventArgs<bool> (oldValue, _force16Colors));
+        }
     }
 
+    /// <summary>Raised when <see cref="Force16Colors"/> changes.</summary>
+    public static event EventHandler<ValueChangedEventArgs<bool>>? Force16ColorsChanged;
+
+    private static string _forceDriver = string.Empty; // Resources/config.json overrides
+
     /// <inheritdoc cref="IApplication.ForceDriver"/>
     [ConfigurationProperty (Scope = typeof (SettingsScope))]
     [Obsolete ("The legacy static Application object is going away.")]
     public static string ForceDriver
     {
-        get => ApplicationImpl.Instance.ForceDriver;
+        get => _forceDriver;
         set
         {
-            if (!string.IsNullOrEmpty (ApplicationImpl.Instance.ForceDriver) && value != Driver?.GetName ())
-            {
-                // ForceDriver cannot be changed if it has a valid value
-                return;
-            }
-
-            if (ApplicationImpl.Instance.Initialized && value != Driver?.GetName ())
-            {
-                throw new InvalidOperationException ($"The {nameof (ForceDriver)} can only be set before initialized.");
-            }
-
-            ApplicationImpl.Instance.ForceDriver = value;
+            string oldValue = _forceDriver;
+            _forceDriver = value;
+            ForceDriverChanged?.Invoke (null, new ValueChangedEventArgs<string> (oldValue, _forceDriver));
         }
     }
 
+    /// <summary>Raised when <see cref="ForceDriver"/> changes.</summary>
+    public static event EventHandler<ValueChangedEventArgs<string>>? ForceDriverChanged;
+
     /// <inheritdoc cref="IApplication.Sixel"/>
     [Obsolete ("The legacy static Application object is going away.")] 
     public static List<SixelToRender> Sixel => ApplicationImpl.Instance.Sixel;

+ 17 - 4
Terminal.Gui/App/Application.Lifecycle.cs

@@ -18,7 +18,15 @@ public static partial class Application // Lifecycle (Init/Shutdown)
     ///     <see cref="IApplication"/> instance for all subsequent application operations.
     /// </remarks>
     /// <returns>A new <see cref="IApplication"/> instance.</returns>
-    public static IApplication Create () { return new ApplicationImpl (); }
+    /// <exception cref="InvalidOperationException">
+    ///     Thrown if the legacy static Application model has already been used in this process.
+    /// </exception>
+    public static IApplication Create ()
+    {
+        ApplicationImpl.MarkInstanceBasedModelUsed ();
+
+        return new ApplicationImpl ();
+    }
 
     /// <inheritdoc cref="IApplication.Init"/>
     [RequiresUnreferencedCode ("AOT")]
@@ -26,6 +34,7 @@ public static partial class Application // Lifecycle (Init/Shutdown)
     [Obsolete ("The legacy static Application object is going away.")]
     public static void Init (string? driverName = null)
     {
+        //Debug.Fail ("Application.Init() called - parallelizable tests should not use legacy static Application model");
         ApplicationImpl.Instance.Init (driverName ?? ForceDriver);
     }
 
@@ -35,8 +44,8 @@ public static partial class Application // Lifecycle (Init/Shutdown)
     [Obsolete ("The legacy static Application object is going away.")]
     public static int? MainThreadId
     {
-        get => ((ApplicationImpl)ApplicationImpl.Instance).MainThreadId;
-        set => ((ApplicationImpl)ApplicationImpl.Instance).MainThreadId = value;
+        get => ApplicationImpl.Instance.MainThreadId;
+        internal set => ApplicationImpl.Instance.MainThreadId = value;
     }
 
     /// <inheritdoc cref="IApplication.Shutdown"/>
@@ -65,5 +74,9 @@ public static partial class Application // Lifecycle (Init/Shutdown)
     // guaranteeing that the state of this singleton is deterministic when Init
     // starts running and after Shutdown returns.
     [Obsolete ("The legacy static Application object is going away.")]
-    internal static void ResetState (bool ignoreDisposed = false) => ApplicationImpl.Instance?.ResetState (ignoreDisposed);
+    internal static void ResetState (bool ignoreDisposed = false)
+    {
+        // Use the static reset method to bypass the fence check
+        ApplicationImpl.ResetStateStatic (ignoreDisposed);
+    }
 }

+ 12 - 2
Terminal.Gui/App/Application.Mouse.cs

@@ -4,15 +4,25 @@ namespace Terminal.Gui.App;
 
 public static partial class Application // Mouse handling
 {
+    private static bool _isMouseDisabled = false; // Resources/config.json overrides
+
     /// <summary>Disable or enable the mouse. The mouse is enabled by default.</summary>
     [ConfigurationProperty (Scope = typeof (SettingsScope))]
     [Obsolete ("The legacy static Application object is going away.")]
     public static bool IsMouseDisabled
     {
-        get => Mouse.IsMouseDisabled;
-        set => Mouse.IsMouseDisabled = value;
+        get => _isMouseDisabled;
+        set
+        {
+            bool oldValue = _isMouseDisabled;
+            _isMouseDisabled = value;
+            IsMouseDisabledChanged?.Invoke (null, new ValueChangedEventArgs<bool> (oldValue, _isMouseDisabled));
+        }
     }
 
+    /// <summary>Raised when <see cref="IsMouseDisabled"/> changes.</summary>
+    public static event EventHandler<ValueChangedEventArgs<bool>>? IsMouseDisabledChanged;
+
     /// <summary>
     ///     Gets the <see cref="IMouse"/> instance that manages mouse event handling and state.
     /// </summary>

+ 49 - 9
Terminal.Gui/App/Application.Navigation.cs

@@ -13,22 +13,42 @@ public static partial class Application // Navigation stuff
         internal set => ApplicationImpl.Instance.Navigation = value;
     }
 
+    private static Key _nextTabGroupKey = Key.F6; // Resources/config.json overrides
+
     /// <summary>Alternative key to navigate forwards through views. Ctrl+Tab is the primary key.</summary>
     [ConfigurationProperty (Scope = typeof (SettingsScope))]
-      [Obsolete ("The legacy static Application object is going away.")]public static Key NextTabGroupKey
+    public static Key NextTabGroupKey
     {
-        get => ApplicationImpl.Instance.Keyboard.NextTabGroupKey;
-        set => ApplicationImpl.Instance.Keyboard.NextTabGroupKey = value;
+        get => _nextTabGroupKey;
+        set
+        {
+            Key oldValue = _nextTabGroupKey;
+            _nextTabGroupKey = value;
+            NextTabGroupKeyChanged?.Invoke (null, new ValueChangedEventArgs<Key> (oldValue, _nextTabGroupKey));
+        }
     }
 
+    /// <summary>Raised when <see cref="NextTabGroupKey"/> changes.</summary>
+    public static event EventHandler<ValueChangedEventArgs<Key>>? NextTabGroupKeyChanged;
+
+    private static Key _nextTabKey = Key.Tab; // Resources/config.json overrides
+
     /// <summary>Alternative key to navigate forwards through views. Tab is the primary key.</summary>
     [ConfigurationProperty (Scope = typeof (SettingsScope))]
     public static Key NextTabKey
     {
-        get => ApplicationImpl.Instance.Keyboard.NextTabKey;
-        set => ApplicationImpl.Instance.Keyboard.NextTabKey = value;
+        get => _nextTabKey;
+        set
+        {
+            Key oldValue = _nextTabKey;
+            _nextTabKey = value;
+            NextTabKeyChanged?.Invoke (null, new ValueChangedEventArgs<Key> (oldValue, _nextTabKey));
+        }
     }
 
+    /// <summary>Raised when <see cref="NextTabKey"/> changes.</summary>
+    public static event EventHandler<ValueChangedEventArgs<Key>>? NextTabKeyChanged;
+
     /// <summary>
     ///     Raised when the user releases a key.
     ///     <para>
@@ -48,19 +68,39 @@ public static partial class Application // Navigation stuff
         remove => ApplicationImpl.Instance.Keyboard.KeyUp -= value;
     }
 
+    private static Key _prevTabGroupKey = Key.F6.WithShift; // Resources/config.json overrides
+
     /// <summary>Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key.</summary>
     [ConfigurationProperty (Scope = typeof (SettingsScope))]
     public static Key PrevTabGroupKey
     {
-        get => ApplicationImpl.Instance.Keyboard.PrevTabGroupKey;
-        set => ApplicationImpl.Instance.Keyboard.PrevTabGroupKey = value;
+        get => _prevTabGroupKey;
+        set
+        {
+            Key oldValue = _prevTabGroupKey;
+            _prevTabGroupKey = value;
+            PrevTabGroupKeyChanged?.Invoke (null, new ValueChangedEventArgs<Key> (oldValue, _prevTabGroupKey));
+        }
     }
 
+    /// <summary>Raised when <see cref="PrevTabGroupKey"/> changes.</summary>
+    public static event EventHandler<ValueChangedEventArgs<Key>>? PrevTabGroupKeyChanged;
+
+    private static Key _prevTabKey = Key.Tab.WithShift; // Resources/config.json overrides
+
     /// <summary>Alternative key to navigate backwards through views. Shift+Tab is the primary key.</summary>
     [ConfigurationProperty (Scope = typeof (SettingsScope))]
     public static Key PrevTabKey
     {
-        get => ApplicationImpl.Instance.Keyboard.PrevTabKey;
-        set => ApplicationImpl.Instance.Keyboard.PrevTabKey = value;
+        get => _prevTabKey;
+        set
+        {
+            Key oldValue = _prevTabKey;
+            _prevTabKey = value;
+            PrevTabKeyChanged?.Invoke (null, new ValueChangedEventArgs<Key> (oldValue, _prevTabKey));
+        }
     }
+
+    /// <summary>Raised when <see cref="PrevTabKey"/> changes.</summary>
+    public static event EventHandler<ValueChangedEventArgs<Key>>? PrevTabKeyChanged;
 }

+ 25 - 5
Terminal.Gui/App/Application.Run.cs

@@ -4,22 +4,42 @@ namespace Terminal.Gui.App;
 
 public static partial class Application // Run (Begin -> Run -> Layout/Draw -> End -> Stop)
 {
+    private static Key _quitKey = Key.Esc; // Resources/config.json overrides
+
     /// <summary>Gets or sets the key to quit the application.</summary>
     [ConfigurationProperty (Scope = typeof (SettingsScope))]
     public static Key QuitKey
     {
-        get => ApplicationImpl.Instance.Keyboard.QuitKey;
-        set => ApplicationImpl.Instance.Keyboard.QuitKey = value;
+        get => _quitKey;
+        set
+        {
+            Key oldValue = _quitKey;
+            _quitKey = value;
+            QuitKeyChanged?.Invoke (null, new ValueChangedEventArgs<Key> (oldValue, _quitKey));
+        }
     }
 
+    /// <summary>Raised when <see cref="QuitKey"/> changes.</summary>
+    public static event EventHandler<ValueChangedEventArgs<Key>>? QuitKeyChanged;
+
+    private static Key _arrangeKey = Key.F5.WithCtrl; // Resources/config.json overrides
+
     /// <summary>Gets or sets the key to activate arranging views using the keyboard.</summary>
     [ConfigurationProperty (Scope = typeof (SettingsScope))]
     public static Key ArrangeKey
     {
-        get => ApplicationImpl.Instance.Keyboard.ArrangeKey;
-        set => ApplicationImpl.Instance.Keyboard.ArrangeKey = value;
+        get => _arrangeKey;
+        set
+        {
+            Key oldValue = _arrangeKey;
+            _arrangeKey = value;
+            ArrangeKeyChanged?.Invoke (null, new ValueChangedEventArgs<Key> (oldValue, _arrangeKey));
+        }
     }
 
+    /// <summary>Raised when <see cref="ArrangeKey"/> changes.</summary>
+    public static event EventHandler<ValueChangedEventArgs<Key>>? ArrangeKeyChanged;
+
     /// <inheritdoc cref="IApplication.Begin(IRunnable)"/>
     [Obsolete ("The legacy static Application object is going away.")]
     public static SessionToken Begin (Toplevel toplevel) => ApplicationImpl.Instance.Begin (toplevel);
@@ -88,7 +108,7 @@ public static partial class Application // Run (Begin -> Run -> Layout/Draw -> E
 
     /// <inheritdoc cref="IApplication.Iteration"/>
     [Obsolete ("The legacy static Application object is going away.")]
-    public static event EventHandler<IterationEventArgs>? Iteration
+    public static event EventHandler<EventArgs<IApplication?>>? Iteration
     {
         add => ApplicationImpl.Instance.Iteration += value;
         remove => ApplicationImpl.Instance.Iteration -= value;

+ 1 - 1
Terminal.Gui/App/Application.Current.cs → Terminal.Gui/App/Application.TopRunnable.cs

@@ -2,7 +2,7 @@ using System.Collections.Concurrent;
 
 namespace Terminal.Gui.App;
 
-public static partial class Application // Current handling
+public static partial class Application // TopRunnable handling
 {
     /// <inheritdoc cref="IApplication.SessionStack"/>
     [Obsolete ("The legacy static Application object is going away.")] public static ConcurrentStack<Toplevel> SessionStack => ApplicationImpl.Instance.SessionStack;

+ 103 - 49
Terminal.Gui/App/ApplicationImpl.Lifecycle.cs

@@ -1,10 +1,14 @@
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
+using System.Reflection;
 
 namespace Terminal.Gui.App;
 
 public partial class ApplicationImpl
 {
+    /// <inheritdoc />
+    public int? MainThreadId { get; set; }
+
     /// <inheritdoc/>
     public bool Initialized { get; set; }
 
@@ -23,6 +27,29 @@ public partial class ApplicationImpl
             throw new InvalidOperationException ("Init called multiple times without Shutdown");
         }
 
+        // Thread-safe fence check: Ensure we're not mixing application models
+        // Use lock to make check-and-set atomic
+        lock (_modelUsageLock)
+        {
+            // If this is a legacy static instance and instance-based model was used, throw
+            if (this == _instance && ModelUsage == ApplicationModelUsage.InstanceBased)
+            {
+                throw new InvalidOperationException (ERROR_LEGACY_AFTER_MODERN);
+            }
+
+            // If this is an instance-based instance and legacy static model was used, throw
+            if (this != _instance && ModelUsage == ApplicationModelUsage.LegacyStatic)
+            {
+                throw new InvalidOperationException (ERROR_MODERN_AFTER_LEGACY);
+            }
+
+            // If no model has been set yet, set it now based on which instance this is
+            if (ModelUsage == ApplicationModelUsage.None)
+            {
+                ModelUsage = this == _instance ? ApplicationModelUsage.LegacyStatic : ApplicationModelUsage.InstanceBased;
+            }
+        }
+
         if (!string.IsNullOrWhiteSpace (driverName))
         {
             _driverName = driverName;
@@ -41,26 +68,24 @@ public partial class ApplicationImpl
 
         // Preserve existing keyboard settings if they exist
         bool hasExistingKeyboard = _keyboard is { };
-        Key existingQuitKey = _keyboard?.QuitKey ?? Key.Esc;
-        Key existingArrangeKey = _keyboard?.ArrangeKey ?? Key.F5.WithCtrl;
-        Key existingNextTabKey = _keyboard?.NextTabKey ?? Key.Tab;
-        Key existingPrevTabKey = _keyboard?.PrevTabKey ?? Key.Tab.WithShift;
-        Key existingNextTabGroupKey = _keyboard?.NextTabGroupKey ?? Key.F6;
-        Key existingPrevTabGroupKey = _keyboard?.PrevTabGroupKey ?? Key.F6.WithShift;
+        Key existingQuitKey = _keyboard?.QuitKey ?? Application.QuitKey;
+        Key existingArrangeKey = _keyboard?.ArrangeKey ?? Application.ArrangeKey;
+        Key existingNextTabKey = _keyboard?.NextTabKey ?? Application.NextTabKey;
+        Key existingPrevTabKey = _keyboard?.PrevTabKey ?? Application.PrevTabKey;
+        Key existingNextTabGroupKey = _keyboard?.NextTabGroupKey ?? Application.NextTabGroupKey;
+        Key existingPrevTabGroupKey = _keyboard?.PrevTabGroupKey ?? Application.PrevTabGroupKey;
 
         // Reset keyboard to ensure fresh state with default bindings
         _keyboard = new KeyboardImpl { App = this };
 
-        // Restore previously set keys if they existed and were different from defaults
-        if (hasExistingKeyboard)
-        {
-            _keyboard.QuitKey = existingQuitKey;
-            _keyboard.ArrangeKey = existingArrangeKey;
-            _keyboard.NextTabKey = existingNextTabKey;
-            _keyboard.PrevTabKey = existingPrevTabKey;
-            _keyboard.NextTabGroupKey = existingNextTabGroupKey;
-            _keyboard.PrevTabGroupKey = existingPrevTabGroupKey;
-        }
+        // Sync keys from Application static properties (or existing keyboard if it had custom values)
+        // This ensures we respect any Application.QuitKey etc changes made before Init()
+        _keyboard.QuitKey = existingQuitKey;
+        _keyboard.ArrangeKey = existingArrangeKey;
+        _keyboard.NextTabKey = existingNextTabKey;
+        _keyboard.PrevTabKey = existingPrevTabKey;
+        _keyboard.NextTabGroupKey = existingNextTabGroupKey;
+        _keyboard.PrevTabGroupKey = existingPrevTabGroupKey;
 
         CreateDriver (_driverName);
         Screen = Driver!.Screen;
@@ -85,8 +110,8 @@ public partial class ApplicationImpl
         if (runnableToDispose is { })
         {
             // Extract the result using reflection to get the Result property value
-            var resultProperty = runnableToDispose.GetType().GetProperty("Result");
-            result = resultProperty?.GetValue(runnableToDispose);
+            PropertyInfo? resultProperty = runnableToDispose.GetType ().GetProperty ("Result");
+            result = resultProperty?.GetValue (runnableToDispose);
         }
 
         // Stop the coordinator if running
@@ -115,8 +140,9 @@ public partial class ApplicationImpl
         {
             if (runnableToDispose is IDisposable disposable)
             {
-                disposable.Dispose();
+                disposable.Dispose ();
             }
+
             FrameworkOwnedRunnable = null;
         }
 
@@ -140,36 +166,6 @@ public partial class ApplicationImpl
         return result;
     }
 
-#if DEBUG
-    /// <summary>
-    ///     DEBUG ONLY: Asserts that an event has no remaining subscribers.
-    /// </summary>
-    /// <param name="eventName">The name of the event for diagnostic purposes.</param>
-    /// <param name="eventDelegate">The event delegate to check.</param>
-    private static void AssertNoEventSubscribers (string eventName, Delegate? eventDelegate)
-    {
-        if (eventDelegate is null)
-        {
-            return;
-        }
-
-        Delegate [] subscribers = eventDelegate.GetInvocationList ();
-
-        if (subscribers.Length > 0)
-        {
-            string subscriberInfo = string.Join (
-                                                 ", ",
-                                                 subscribers.Select (d => $"{d.Method.DeclaringType?.Name}.{d.Method.Name}"
-                                                                    )
-                                                );
-
-            Debug.Fail (
-                        $"Application.{eventName} has {subscribers.Length} remaining subscriber(s) after Shutdown: {subscriberInfo}"
-                       );
-        }
-    }
-#endif
-
     /// <inheritdoc/>
     public void ResetState (bool ignoreDisposed = false)
     {
@@ -241,6 +237,17 @@ public partial class ApplicationImpl
         ClearScreenNextIteration = false;
 
         // === 6. Reset input systems ===
+        // Dispose keyboard and mouse to unsubscribe from events
+        if (_keyboard is IDisposable keyboardDisposable)
+        {
+            keyboardDisposable.Dispose ();
+        }
+
+        if (_mouse is IDisposable mouseDisposable)
+        {
+            mouseDisposable.Dispose ();
+        }
+
         // Mouse and Keyboard will be lazy-initialized on next access
         _mouse = null;
         _keyboard = null;
@@ -273,10 +280,57 @@ public partial class ApplicationImpl
         // gui.cs does no longer process any callbacks. See #1084 for more details:
         // (https://github.com/gui-cs/Terminal.Gui/issues/1084).
         SynchronizationContext.SetSynchronizationContext (null);
+
+        // === 12. Unsubscribe from Application static property change events ===
+        UnsubscribeApplicationEvents ();
     }
 
     /// <summary>
     ///     Raises the <see cref="InitializedChanged"/> event.
     /// </summary>
     internal void RaiseInitializedChanged (object sender, EventArgs<bool> e) { InitializedChanged?.Invoke (sender, e); }
+
+#if DEBUG
+    /// <summary>
+    ///     DEBUG ONLY: Asserts that an event has no remaining subscribers.
+    /// </summary>
+    /// <param name="eventName">The name of the event for diagnostic purposes.</param>
+    /// <param name="eventDelegate">The event delegate to check.</param>
+    private static void AssertNoEventSubscribers (string eventName, Delegate? eventDelegate)
+    {
+        if (eventDelegate is null)
+        {
+            return;
+        }
+
+        Delegate [] subscribers = eventDelegate.GetInvocationList ();
+
+        if (subscribers.Length > 0)
+        {
+            string subscriberInfo = string.Join (
+                                                 ", ",
+                                                 subscribers.Select (d => $"{d.Method.DeclaringType?.Name}.{d.Method.Name}"
+                                                                    )
+                                                );
+
+            Debug.Fail (
+                        $"Application.{eventName} has {subscribers.Length} remaining subscriber(s) after Shutdown: {subscriberInfo}"
+                       );
+        }
+    }
+#endif
+
+    // Event handlers for Application static property changes
+    private void OnForce16ColorsChanged (object? sender, ValueChangedEventArgs<bool> e) { Force16Colors = e.NewValue; }
+
+    private void OnForceDriverChanged (object? sender, ValueChangedEventArgs<string> e) { ForceDriver = e.NewValue; }
+
+    /// <summary>
+    ///     Unsubscribes from Application static property change events.
+    /// </summary>
+    private void UnsubscribeApplicationEvents ()
+    {
+        Application.Force16ColorsChanged -= OnForce16ColorsChanged;
+        Application.ForceDriverChanged -= OnForceDriverChanged;
+    }
 }

+ 2 - 11
Terminal.Gui/App/ApplicationImpl.Run.cs

@@ -5,15 +5,6 @@ namespace Terminal.Gui.App;
 
 public partial class ApplicationImpl
 {
-    /// <summary>
-    ///     INTERNAL: Gets or sets the managed thread ID of the application's main UI thread, which is set during
-    ///     <see cref="Init"/> and used to determine if code is executing on the main thread.
-    /// </summary>
-    /// <value>
-    ///     The managed thread ID of the main UI thread, or <see langword="null"/> if the application is not initialized.
-    /// </value>
-    internal int? MainThreadId { get; set; }
-
     #region Begin->Run->Stop->End
 
     // TODO: This API is not used anywhere; it can be deleted
@@ -156,11 +147,11 @@ public partial class ApplicationImpl
     /// <inheritdoc/>
     public void RaiseIteration ()
     {
-        Iteration?.Invoke (null, new ());
+        Iteration?.Invoke (null, new (this));
     }
 
     /// <inheritdoc/>
-    public event EventHandler<IterationEventArgs>? Iteration;
+    public event EventHandler<EventArgs<IApplication?>>? Iteration;
 
     /// <inheritdoc/>
     [RequiresUnreferencedCode ("AOT")]

+ 136 - 12
Terminal.Gui/App/ApplicationImpl.cs

@@ -9,23 +9,72 @@ namespace Terminal.Gui.App;
 public partial class ApplicationImpl : IApplication
 {
     /// <summary>
-    ///     INTERNAL: Creates a new instance of the Application backend.
+    ///     INTERNAL: Creates a new instance of the Application backend and subscribes to Application configuration property
+    ///     events.
     /// </summary>
-    internal ApplicationImpl () { }
+    internal ApplicationImpl ()
+    {
+        // Subscribe to Application static property change events
+        Application.Force16ColorsChanged += OnForce16ColorsChanged;
+        Application.ForceDriverChanged += OnForceDriverChanged;
+    }
 
     /// <summary>
     ///     INTERNAL: Creates a new instance of the Application backend.
     /// </summary>
     /// <param name="componentFactory"></param>
-    internal ApplicationImpl (IComponentFactory componentFactory) { _componentFactory = componentFactory; }
+    internal ApplicationImpl (IComponentFactory componentFactory) : this () { _componentFactory = componentFactory; }
+
+    private string? _driverName;
+
+    #region Clipboard
+
+    /// <inheritdoc/>
+    public IClipboard? Clipboard => Driver?.Clipboard;
+
+    #endregion Clipboard
+
+    /// <inheritdoc/>
+    public new string ToString () => Driver?.ToString () ?? string.Empty;
 
     #region Singleton
 
+    /// <summary>
+    ///     Lock object for synchronizing access to ModelUsage and _instance.
+    /// </summary>
+    private static readonly object _modelUsageLock = new ();
+
+    /// <summary>
+    ///     Tracks which application model has been used in this process.
+    /// </summary>
+    public static ApplicationModelUsage ModelUsage { get; private set; } = ApplicationModelUsage.None;
+
+    /// <summary>
+    ///     Error message for when trying to use modern model after legacy static model.
+    /// </summary>
+    internal const string ERROR_MODERN_AFTER_LEGACY =
+        "Cannot use modern instance-based model (Application.Create) after using legacy static Application model (Application.Init/ApplicationImpl.Instance). "
+        + "Use only one model per process.";
+
+    /// <summary>
+    ///     Error message for when trying to use legacy static model after modern model.
+    /// </summary>
+    internal const string ERROR_LEGACY_AFTER_MODERN =
+        "Cannot use legacy static Application model (Application.Init/ApplicationImpl.Instance) after using modern instance-based model (Application.Create). "
+        + "Use only one model per process.";
+
     /// <summary>
     ///     Configures the singleton instance of <see cref="Application"/> to use the specified backend implementation.
     /// </summary>
     /// <param name="app"></param>
-    public static void SetInstance (IApplication? app) { _instance = app; }
+    public static void SetInstance (IApplication? app)
+    {
+        lock (_modelUsageLock)
+        {
+            ModelUsage = ApplicationModelUsage.LegacyStatic;
+            _instance = app;
+        }
+    }
 
     // Private static readonly Lazy instance of Application
     private static IApplication? _instance;
@@ -33,11 +82,91 @@ public partial class ApplicationImpl : IApplication
     /// <summary>
     ///     Gets the currently configured backend implementation of <see cref="Application"/> gateway methods.
     /// </summary>
-    public static IApplication Instance => _instance ??= new ApplicationImpl ();
+    public static IApplication Instance
+    {
+        get
+        {
+            //Debug.Fail ("ApplicationImpl.Instance accessed - parallelizable tests should not use legacy static Application model");
 
-    #endregion Singleton
+            // Thread-safe: Use lock to make check-and-create atomic
+            lock (_modelUsageLock)
+            {
+                // If an instance already exists, return it without fence checking
+                // This allows for cleanup/reset operations
+                if (_instance is { })
+                {
+                    return _instance;
+                }
+
+                // Check if the instance-based model has already been used
+                if (ModelUsage == ApplicationModelUsage.InstanceBased)
+                {
+                    throw new InvalidOperationException (ERROR_LEGACY_AFTER_MODERN);
+                }
+
+                // Mark the usage and create the instance
+                ModelUsage = ApplicationModelUsage.LegacyStatic;
+
+                return _instance = new ApplicationImpl ();
+            }
+        }
+    }
 
-    private string? _driverName;
+    /// <summary>
+    ///     INTERNAL: Marks that the instance-based model has been used. Called by Application.Create().
+    /// </summary>
+    internal static void MarkInstanceBasedModelUsed ()
+    {
+        lock (_modelUsageLock)
+        {
+            // Check if the legacy static model has already been initialized
+            if (ModelUsage == ApplicationModelUsage.LegacyStatic && _instance?.Initialized == true)
+            {
+                throw new InvalidOperationException (ERROR_MODERN_AFTER_LEGACY);
+            }
+
+            ModelUsage = ApplicationModelUsage.InstanceBased;
+        }
+    }
+
+    /// <summary>
+    ///     INTERNAL: Resets the model usage tracking. Only for testing purposes.
+    /// </summary>
+    internal static void ResetModelUsageTracking ()
+    {
+        lock (_modelUsageLock)
+        {
+            ModelUsage = ApplicationModelUsage.None;
+            _instance = null;
+        }
+    }
+
+    /// <summary>
+    ///     INTERNAL: Resets state without going through the fence-checked Instance property.
+    ///     Used by Application.ResetState() to allow cleanup regardless of which model was used.
+    /// </summary>
+    internal static void ResetStateStatic (bool ignoreDisposed = false)
+    {
+        // If an instance exists, reset it
+        _instance?.ResetState (ignoreDisposed);
+
+        // Reset Application static properties to their defaults
+        // This ensures tests start with clean state
+        Application.ForceDriver = string.Empty;
+        Application.Force16Colors = false;
+        Application.IsMouseDisabled = false;
+        Application.QuitKey = Key.Esc;
+        Application.ArrangeKey = Key.F5.WithCtrl;
+        Application.NextTabGroupKey = Key.F6;
+        Application.NextTabKey = Key.Tab;
+        Application.PrevTabGroupKey = Key.F6.WithShift;
+        Application.PrevTabKey = Key.Tab.WithShift;
+
+        // Always reset the model tracking to allow tests to use either model after reset
+        ResetModelUsageTracking ();
+    }
+
+    #endregion Singleton
 
     #region Input
 
@@ -122,8 +251,6 @@ public partial class ApplicationImpl : IApplication
         }
     }
 
-    // BUGBUG: Technically, this is not the full lst of sessions. There be dragons here, e.g. see how Toplevel.Id is used. What
-
     /// <inheritdoc/>
     public ConcurrentStack<Toplevel> SessionStack { get; } = new ();
 
@@ -137,7 +264,4 @@ public partial class ApplicationImpl : IApplication
     public IRunnable? FrameworkOwnedRunnable { get; set; }
 
     #endregion View Management
-
-    /// <inheritdoc/>
-    public new string ToString () => Driver?.ToString () ?? string.Empty;
 }

+ 16 - 0
Terminal.Gui/App/ApplicationModelUsage.cs

@@ -0,0 +1,16 @@
+namespace Terminal.Gui.App;
+
+/// <summary>
+///     Defines the different application usage models.
+/// </summary>
+public enum ApplicationModelUsage
+{
+    /// <summary>No model has been used yet.</summary>
+    None,
+
+    /// <summary>Legacy static model (Application.Init/ApplicationImpl.Instance).</summary>
+    LegacyStatic,
+
+    /// <summary>Modern instance-based model (Application.Create).</summary>
+    InstanceBased
+}

+ 1 - 1
Terminal.Gui/App/ApplicationNavigation.cs

@@ -13,7 +13,7 @@ public class ApplicationNavigation
     /// </summary>
     public ApplicationNavigation ()
     {
-        // TODO: Move navigation key bindings here from AddApplicationKeyBindings
+        // TODO: Move navigation key bindings here from KeyboardImpl
     }
 
     /// <summary>

+ 1 - 1
Terminal.Gui/App/ApplicationRunnableExtensions.cs

@@ -5,7 +5,7 @@ namespace Terminal.Gui.App;
 /// </summary>
 /// <remarks>
 ///     These extensions provide convenience methods for wrapping views in <see cref="RunnableWrapper{TView, TResult}"/>
-///     and running them in a single call, similar to how <see cref="IApplication.Run{TRunnable}()"/> works.
+///     and running them in a single call, similar to how <see cref="IApplication.Run(Func{Exception, bool}, string)"/> works.
 /// </remarks>
 public static class ApplicationRunnableExtensions
 {

+ 32 - 0
Terminal.Gui/App/Clipboard/Clipboard.cs

@@ -2,6 +2,9 @@ namespace Terminal.Gui.App;
 
 /// <summary>Provides cut, copy, and paste support for the OS clipboard.</summary>
 /// <remarks>
+///     <para>
+///         <b>DEPRECATED:</b> This static class is obsolete. Use <see cref="IApplication.Clipboard"/> instead.
+///     </para>
 ///     <para>On Windows, the <see cref="Clipboard"/> class uses the Windows Clipboard APIs via P/Invoke.</para>
 ///     <para>
 ///         On Linux, when not running under Windows Subsystem for Linux (WSL), the <see cref="Clipboard"/> class uses
@@ -16,6 +19,7 @@ namespace Terminal.Gui.App;
 ///         the Mac clipboard APIs vai P/Invoke.
 ///     </para>
 /// </remarks>
+[Obsolete ("Use IApplication.Clipboard instead. The static Clipboard class will be removed in a future release.")]
 public static class Clipboard
 {
     private static string? _contents = string.Empty;
@@ -65,4 +69,32 @@ public static class Clipboard
     /// <summary>Returns true if the environmental dependencies are in place to interact with the OS clipboard.</summary>
     /// <remarks></remarks>
     public static bool IsSupported => Application.Driver?.Clipboard?.IsSupported ?? false;
+
+    /// <summary>Gets the OS clipboard data if possible.</summary>
+    /// <param name="result">The clipboard data if successful.</param>
+    /// <returns><see langword="true"/> if the clipboard data was retrieved successfully; otherwise, <see langword="false"/>.</returns>
+    public static bool TryGetClipboardData (out string result)
+    {
+        result = string.Empty;
+
+        if (IsSupported && Application.Driver?.Clipboard is { })
+        {
+            return Application.Driver.Clipboard.TryGetClipboardData (out result);
+        }
+
+        return false;
+    }
+
+    /// <summary>Sets the OS clipboard data if possible.</summary>
+    /// <param name="text">The text to set.</param>
+    /// <returns><see langword="true"/> if the clipboard data was set successfully; otherwise, <see langword="false"/>.</returns>
+    public static bool TrySetClipboardData (string text)
+    {
+        if (IsSupported && Application.Driver?.Clipboard is { })
+        {
+            return Application.Driver.Clipboard.TrySetClipboardData (text);
+        }
+
+        return false;
+    }
 }

+ 27 - 4
Terminal.Gui/App/IApplication.cs

@@ -39,6 +39,16 @@ public interface IApplication
 
     #region Initialization and Shutdown
 
+    /// <summary>
+    ///     Gets or sets the managed thread ID of the application's main UI thread, which is set during
+    ///     <see cref="Init"/> and used to determine if code is executing on the main thread.
+    /// </summary>
+    /// <value>
+    ///     The managed thread ID of the main UI thread, or <see langword="null"/> if the application is not initialized.
+    /// </value>
+    public int? MainThreadId { get; internal set; }
+
+
     /// <summary>Initializes a new instance of <see cref="Terminal.Gui"/> Application.</summary>
     /// <param name="driverName">
     ///     The short name (e.g. "dotnet", "windows", "unix", or "fake") of the
@@ -218,7 +228,7 @@ public interface IApplication
     [RequiresUnreferencedCode ("AOT")]
     [RequiresDynamicCode ("AOT")]
     public TView Run<TView> (Func<Exception, bool>? errorHandler = null, string? driverName = null)
-        where TView : Toplevel, new ();
+        where TView : Toplevel, new();
 
     /// <summary>
     ///     Runs a new Session using the provided <see cref="Toplevel"/> view and calling
@@ -273,9 +283,11 @@ public interface IApplication
     ///     <para>
     ///         This event is raised before input processing, timeout callbacks, and rendering occur each iteration.
     ///     </para>
-    ///     <para>See also <see cref="AddTimeout"/> and <see cref="TimedEvents"/>.</para>
+    ///     <para>The event args contain the current application instance.</para>
     /// </remarks>
-    public event EventHandler<IterationEventArgs>? Iteration;
+    /// <seealso cref="AddTimeout"/>
+    /// <seealso cref="TimedEvents"/>.
+    public event EventHandler<EventArgs<IApplication?>>? Iteration;
 
     /// <summary>Runs <paramref name="action"/> on the main UI loop thread.</summary>
     /// <param name="action">The action to be invoked on the main processing thread.</param>
@@ -523,7 +535,7 @@ public interface IApplication
     ///         Supports fluent API: <c>var result = Application.Create().Init().Run&lt;MyView&gt;().Shutdown() as MyResultType</c>
     ///     </para>
     /// </remarks>
-    IApplication Run<TRunnable> (Func<Exception, bool>? errorHandler = null) where TRunnable : IRunnable, new ();
+    IApplication Run<TRunnable> (Func<Exception, bool>? errorHandler = null) where TRunnable : IRunnable, new();
 
     /// <summary>
     ///     Requests that the specified runnable session stop.
@@ -574,6 +586,17 @@ public interface IApplication
     /// </remarks>
     IDriver? Driver { get; set; }
 
+    /// <summary>
+    ///     Gets the clipboard for this application instance.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         Provides access to the OS clipboard through the driver. Returns <see langword="null"/> if
+    ///         <see cref="Driver"/> is not initialized.
+    ///     </para>
+    /// </remarks>
+    IClipboard? Clipboard { get; }
+
     /// <summary>
     ///     Gets or sets whether <see cref="Driver"/> will be forced to output only the 16 colors defined in
     ///     <see cref="ColorName16"/>. The default is <see langword="false"/>, meaning 24-bit (TrueColor) colors will be

+ 0 - 5
Terminal.Gui/App/IterationEventArgs.cs

@@ -1,5 +0,0 @@
-namespace Terminal.Gui.App;
-
-/// <summary>Event arguments for the <see cref="IApplication.Iteration"/> event.</summary>
-public class IterationEventArgs : EventArgs
-{ }

+ 110 - 55
Terminal.Gui/App/Keyboard/KeyboardImpl.cs

@@ -1,7 +1,10 @@
+using System.Collections.Concurrent;
+
 namespace Terminal.Gui.App;
 
 /// <summary>
 ///     INTERNAL: Implements <see cref="IKeyboard"/> to manage keyboard input and key bindings at the Application level.
+///     This implementation is thread-safe for all public operations.
 ///     <para>
 ///         This implementation decouples keyboard handling state from the static <see cref="App"/> class,
 ///         enabling parallelizable unit tests and better testability.
@@ -10,19 +13,61 @@ namespace Terminal.Gui.App;
 ///         See <see cref="IKeyboard"/> for usage details.
 ///     </para>
 /// </summary>
-internal class KeyboardImpl : IKeyboard
+internal class KeyboardImpl : IKeyboard, IDisposable
 {
-    private Key _quitKey = Key.Esc; // Resources/config.json overrides
-    private Key _arrangeKey = Key.F5.WithCtrl; // Resources/config.json overrides
-    private Key _nextTabGroupKey = Key.F6; // Resources/config.json overrides
-    private Key _nextTabKey = Key.Tab; // Resources/config.json overrides
-    private Key _prevTabGroupKey = Key.F6.WithShift; // Resources/config.json overrides
-    private Key _prevTabKey = Key.Tab.WithShift; // Resources/config.json overrides
+    /// <summary>
+    ///     Initializes keyboard bindings and subscribes to Application configuration property events.
+    /// </summary>
+    public KeyboardImpl ()
+    {
+        // DON'T access Application static properties here - they trigger ApplicationImpl.Instance
+        // which sets ModelUsage to LegacyStatic, breaking parallel tests.
+        // These will be initialized from Application static properties in Init() or when accessed.
+
+        // Initialize to reasonable defaults that match Application defaults
+        // These will be updated by property change events if Application properties change
+        _quitKey = Key.Esc;
+        _arrangeKey = Key.F5.WithCtrl;
+        _nextTabGroupKey = Key.F6;
+        _nextTabKey = Key.Tab;
+        _prevTabGroupKey = Key.F6.WithShift;
+        _prevTabKey = Key.Tab.WithShift;
+
+        // Subscribe to Application static property change events
+        // so we get updated if they change
+        Application.QuitKeyChanged += OnQuitKeyChanged;
+        Application.ArrangeKeyChanged += OnArrangeKeyChanged;
+        Application.NextTabGroupKeyChanged += OnNextTabGroupKeyChanged;
+        Application.NextTabKeyChanged += OnNextTabKeyChanged;
+        Application.PrevTabGroupKeyChanged += OnPrevTabGroupKeyChanged;
+        Application.PrevTabKeyChanged += OnPrevTabKeyChanged;
+
+        AddKeyBindings ();
+    }
 
     /// <summary>
-    ///     Commands for Application.
+    ///     Commands for Application. Thread-safe for concurrent access.
     /// </summary>
-    private readonly Dictionary<Command, View.CommandImplementation> _commandImplementations = new ();
+    private readonly ConcurrentDictionary<Command, View.CommandImplementation> _commandImplementations = new ();
+
+    private Key _quitKey;
+    private Key _arrangeKey;
+    private Key _nextTabGroupKey;
+    private Key _nextTabKey;
+    private Key _prevTabGroupKey;
+    private Key _prevTabKey;
+
+    /// <inheritdoc/>
+    public void Dispose ()
+    {
+        // Unsubscribe from Application static property change events
+        Application.QuitKeyChanged -= OnQuitKeyChanged;
+        Application.ArrangeKeyChanged -= OnArrangeKeyChanged;
+        Application.NextTabGroupKeyChanged -= OnNextTabGroupKeyChanged;
+        Application.NextTabKeyChanged -= OnNextTabKeyChanged;
+        Application.PrevTabGroupKeyChanged -= OnPrevTabGroupKeyChanged;
+        Application.PrevTabKeyChanged -= OnPrevTabKeyChanged;
+    }
 
     /// <inheritdoc/>
     public IApplication? App { get; set; }
@@ -102,14 +147,6 @@ internal class KeyboardImpl : IKeyboard
     /// <inheritdoc/>
     public event EventHandler<Key>? KeyUp;
 
-    /// <summary>
-    ///     Initializes keyboard bindings.
-    /// </summary>
-    public KeyboardImpl ()
-    {
-        AddKeyBindings ();
-    }
-
     /// <inheritdoc/>
     public bool RaiseKeyDownEvent (Key key)
     {
@@ -165,7 +202,8 @@ internal class KeyboardImpl : IKeyboard
         }
 
         bool? commandHandled = InvokeCommandsBoundToKey (key);
-        if(commandHandled is true)
+
+        if (commandHandled is true)
         {
             return true;
         }
@@ -188,7 +226,6 @@ internal class KeyboardImpl : IKeyboard
             return true;
         }
 
-
         // TODO: Add Popover support
 
         if (App?.SessionStack is { })
@@ -214,6 +251,7 @@ internal class KeyboardImpl : IKeyboard
     public bool? InvokeCommandsBoundToKey (Key key)
     {
         bool? handled = null;
+
         // Invoke any Application-scoped KeyBindings.
         // The first view that handles the key will stop the loop.
         // foreach (KeyValuePair<Key, KeyBinding> binding in KeyBindings.GetBindings (key))
@@ -264,24 +302,6 @@ internal class KeyboardImpl : IKeyboard
         return null;
     }
 
-    /// <summary>
-    ///     <para>
-    ///         Sets the function that will be invoked for a <see cref="Command"/>.
-    ///     </para>
-    ///     <para>
-    ///         If AddCommand has already been called for <paramref name="command"/> <paramref name="f"/> will
-    ///         replace the old one.
-    ///     </para>
-    /// </summary>
-    /// <remarks>
-    ///     <para>
-    ///         This version of AddCommand is for commands that do not require a <see cref="ICommandContext"/>.
-    ///     </para>
-    /// </remarks>
-    /// <param name="command">The command.</param>
-    /// <param name="f">The function.</param>
-    private void AddCommand (Command command, Func<bool?> f) { _commandImplementations [command] = ctx => f (); }
-
     internal void AddKeyBindings ()
     {
         _commandImplementations.Clear ();
@@ -296,6 +316,7 @@ internal class KeyboardImpl : IKeyboard
                         return true;
                     }
                    );
+
         AddCommand (
                     Command.Suspend,
                     () =>
@@ -305,6 +326,7 @@ internal class KeyboardImpl : IKeyboard
                         return true;
                     }
                    );
+
         AddCommand (
                     Command.NextTabStop,
                     () => App?.Navigation?.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop));
@@ -351,31 +373,64 @@ internal class KeyboardImpl : IKeyboard
                         return false;
                     });
 
-        //SetKeysToHardCodedDefaults ();
-
         // Need to clear after setting the above to ensure actually clear
-        // because set_QuitKey etc.. may call Add
-        KeyBindings.Clear ();
-
-        KeyBindings.Add (QuitKey, Command.Quit);
-        KeyBindings.Add (NextTabKey, Command.NextTabStop);
-        KeyBindings.Add (PrevTabKey, Command.PreviousTabStop);
-        KeyBindings.Add (NextTabGroupKey, Command.NextTabGroup);
-        KeyBindings.Add (PrevTabGroupKey, Command.PreviousTabGroup);
-        KeyBindings.Add (ArrangeKey, Command.Arrange);
-
-        KeyBindings.Add (Key.CursorRight, Command.NextTabStop);
-        KeyBindings.Add (Key.CursorDown, Command.NextTabStop);
-        KeyBindings.Add (Key.CursorLeft, Command.PreviousTabStop);
-        KeyBindings.Add (Key.CursorUp, Command.PreviousTabStop);
+        // because set_QuitKey etc. may call Add
+        //KeyBindings.Clear ();
+
+        // Use ReplaceCommands instead of Add, because it's possible that
+        // during construction the Application static properties changed, and
+        // we added those keys already.
+        KeyBindings.ReplaceCommands (QuitKey, Command.Quit);
+        KeyBindings.ReplaceCommands (NextTabKey, Command.NextTabStop);
+        KeyBindings.ReplaceCommands (PrevTabKey, Command.PreviousTabStop);
+        KeyBindings.ReplaceCommands (NextTabGroupKey, Command.NextTabGroup);
+        KeyBindings.ReplaceCommands (PrevTabGroupKey, Command.PreviousTabGroup);
+        KeyBindings.ReplaceCommands (ArrangeKey, Command.Arrange);
+
+        // TODO: Should these be configurable?
+        KeyBindings.ReplaceCommands (Key.CursorRight, Command.NextTabStop);
+        KeyBindings.ReplaceCommands (Key.CursorDown, Command.NextTabStop);
+        KeyBindings.ReplaceCommands (Key.CursorLeft, Command.PreviousTabStop);
+        KeyBindings.ReplaceCommands (Key.CursorUp, Command.PreviousTabStop);
 
         // TODO: Refresh Key should be configurable
-        KeyBindings.Add (Key.F5, Command.Refresh);
+        KeyBindings.ReplaceCommands (Key.F5, Command.Refresh);
 
         // TODO: Suspend Key should be configurable
         if (Environment.OSVersion.Platform == PlatformID.Unix)
         {
-            KeyBindings.Add (Key.Z.WithCtrl, Command.Suspend);
+            KeyBindings.ReplaceCommands (Key.Z.WithCtrl, Command.Suspend);
         }
     }
+
+    /// <summary>
+    ///     <para>
+    ///         Sets the function that will be invoked for a <see cref="Command"/>.
+    ///     </para>
+    ///     <para>
+    ///         If AddCommand has already been called for <paramref name="command"/> <paramref name="f"/> will
+    ///         replace the old one.
+    ///     </para>
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         This version of AddCommand is for commands that do not require a <see cref="ICommandContext"/>.
+    ///     </para>
+    /// </remarks>
+    /// <param name="command">The command.</param>
+    /// <param name="f">The function.</param>
+    private void AddCommand (Command command, Func<bool?> f) { _commandImplementations [command] = ctx => f (); }
+
+    private void OnArrangeKeyChanged (object? sender, ValueChangedEventArgs<Key> e) { ArrangeKey = e.NewValue; }
+
+    private void OnNextTabGroupKeyChanged (object? sender, ValueChangedEventArgs<Key> e) { NextTabGroupKey = e.NewValue; }
+
+    private void OnNextTabKeyChanged (object? sender, ValueChangedEventArgs<Key> e) { NextTabKey = e.NewValue; }
+
+    private void OnPrevTabGroupKeyChanged (object? sender, ValueChangedEventArgs<Key> e) { PrevTabGroupKey = e.NewValue; }
+
+    private void OnPrevTabKeyChanged (object? sender, ValueChangedEventArgs<Key> e) { PrevTabKey = e.NewValue; }
+
+    // Event handlers for Application static property changes
+    private void OnQuitKeyChanged (object? sender, ValueChangedEventArgs<Key> e) { QuitKey = e.NewValue; }
 }

+ 27 - 4
Terminal.Gui/App/Mouse/MouseImpl.cs

@@ -9,15 +9,25 @@ namespace Terminal.Gui.App;
 ///         enabling better testability and parallel test execution.
 ///     </para>
 /// </summary>
-internal class MouseImpl : IMouse
+internal class MouseImpl : IMouse, IDisposable
 {
     /// <summary>
-    ///     Initializes a new instance of the <see cref="MouseImpl"/> class.
+    ///     Initializes a new instance of the <see cref="MouseImpl"/> class and subscribes to Application configuration property events.
     /// </summary>
-    public MouseImpl () { }
+    public MouseImpl ()
+    {
+        // Subscribe to Application static property change events
+        Application.IsMouseDisabledChanged += OnIsMouseDisabledChanged;
+    }
+
+    private IApplication? _app;
 
     /// <inheritdoc/>
-    public IApplication? App { get; set; }
+    public IApplication? App
+    {
+        get => _app;
+        set => _app = value;
+    }
 
     /// <inheritdoc/>
     public Point? LastMousePosition { get; set; }
@@ -391,4 +401,17 @@ internal class MouseImpl : IMouse
 
         return false;
     }
+
+    // Event handler for Application static property changes
+    private void OnIsMouseDisabledChanged (object? sender, ValueChangedEventArgs<bool> e)
+    {
+        IsMouseDisabled = e.NewValue;
+    }
+
+    /// <inheritdoc/>
+    public void Dispose ()
+    {
+        // Unsubscribe from Application static property change events
+        Application.IsMouseDisabledChanged -= OnIsMouseDisabledChanged;
+    }
 }

+ 2 - 2
Terminal.Gui/App/Runnable/IRunnable.cs

@@ -66,7 +66,7 @@ public interface IRunnable
     /// <summary>
     ///     Raised when <see cref="IsRunning"/> is changing (e.g., when <see cref="IApplication.Begin(IRunnable)"/> or
     ///     <see cref="IApplication.End(RunnableSessionToken)"/> is called).
-    ///     Can be canceled by setting <see cref="CancelEventArgs{T}.Cancel"/> to <see langword="true"/>.
+    ///     Can be canceled by setting `args.Cancel` to <see langword="true"/>.
     /// </summary>
     /// <remarks>
     ///     <para>
@@ -140,7 +140,7 @@ public interface IRunnable
 
     /// <summary>
     ///     Raised when this runnable is about to become modal (top of stack) or cease being modal.
-    ///     Can be canceled by setting <see cref="CancelEventArgs{T}.Cancel"/> to <see langword="true"/>.
+    ///     Can be canceled by setting `args.Cancel` to <see langword="true"/>.
     /// </summary>
     /// <remarks>
     ///     <para>

+ 80 - 4
Terminal.Gui/Configuration/ConfigurationManager.cs

@@ -595,15 +595,53 @@ public static class ConfigurationManager
                                                                                   TypeInfoResolver = SourceGenerationContext.Default
                                                                               });
 
+    private static SourcesManager? _sourcesManager = new ();
+    private static readonly object _sourcesManagerLock = new ();
+
     /// <summary>
     ///     Gets the Sources Manager - manages the loading of configuration sources from files and resources.
     /// </summary>
-    public static SourcesManager? SourcesManager { get; internal set; } = new ();
+    public static SourcesManager? SourcesManager
+    {
+        get
+        {
+            lock (_sourcesManagerLock)
+            {
+                return _sourcesManager;
+            }
+        }
+        internal set
+        {
+            lock (_sourcesManagerLock)
+            {
+                _sourcesManager = value;
+            }
+        }
+    }
+
+    private static string? _runtimeConfig = """{  }""";
+    private static readonly object _runtimeConfigLock = new ();
 
     /// <summary>
     ///     Gets or sets the in-memory config.json. See <see cref="ConfigLocations.Runtime"/>.
     /// </summary>
-    public static string? RuntimeConfig { get; set; } = """{  }""";
+    public static string? RuntimeConfig
+    {
+        get
+        {
+            lock (_runtimeConfigLock)
+            {
+                return _runtimeConfig;
+            }
+        }
+        set
+        {
+            lock (_runtimeConfigLock)
+            {
+                _runtimeConfig = value;
+            }
+        }
+    }
 
     [SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
     private static readonly string _configFilename = "config.json";
@@ -678,13 +716,32 @@ public static class ConfigurationManager
     [SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
     internal static StringBuilder _jsonErrors = new ();
 
+    private static bool? _throwOnJsonErrors = false;
+    private static readonly object _throwOnJsonErrorsLock = new ();
+
     /// <summary>
     ///     Gets or sets whether the <see cref="ConfigurationManager"/> should throw an exception if it encounters an
     ///     error on deserialization. If <see langword="false"/> (the default), the error is logged and printed to the console
     ///     when <see cref="Application.Shutdown"/> is called.
     /// </summary>
     [ConfigurationProperty (Scope = typeof (SettingsScope))]
-    public static bool? ThrowOnJsonErrors { get; set; } = false;
+    public static bool? ThrowOnJsonErrors
+    {
+        get
+        {
+            lock (_throwOnJsonErrorsLock)
+            {
+                return _throwOnJsonErrors;
+            }
+        }
+        set
+        {
+            lock (_throwOnJsonErrorsLock)
+            {
+                _throwOnJsonErrors = value;
+            }
+        }
+    }
 
 #pragma warning disable IDE1006 // Naming Styles
     private static readonly object _jsonErrorsLock = new ();
@@ -758,8 +815,27 @@ public static class ConfigurationManager
         return JsonSerializer.Serialize (emptyScope, typeof (SettingsScope), SerializerContext!);
     }
 
+    private static string _appName = Assembly.GetEntryAssembly ()?.FullName?.Split (',') [0]?.Trim ()!;
+    private static readonly object _appNameLock = new ();
+
     /// <summary>Name of the running application. By default, this property is set to the application's assembly name.</summary>
-    public static string AppName { get; set; } = Assembly.GetEntryAssembly ()?.FullName?.Split (',') [0]?.Trim ()!;
+    public static string AppName
+    {
+        get
+        {
+            lock (_appNameLock)
+            {
+                return _appName;
+            }
+        }
+        set
+        {
+            lock (_appNameLock)
+            {
+                _appName = value;
+            }
+        }
+    }
 
     /// <summary>
     ///     INTERNAL: Retrieves all uninitialized configuration properties that belong to a specific scope from the cache.

+ 5 - 7
Terminal.Gui/Configuration/SourcesManager.cs

@@ -1,4 +1,5 @@
-using System.Diagnostics;
+using System.Collections.Concurrent;
+using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Reflection;
 using System.Text.Json;
@@ -13,7 +14,7 @@ public class SourcesManager
     /// <summary>
     ///     Provides a map from each of the <see cref="ConfigLocations"/> to file system and resource paths that have been loaded by <see cref="ConfigurationManager"/>.
     /// </summary>
-    public Dictionary<ConfigLocations, string> Sources { get; } = new ();
+    public ConcurrentDictionary<ConfigLocations, string> Sources { get; } = new ();
 
     /// <summary>INTERNAL: Loads <paramref name="stream"/> into the specified <see cref="SettingsScope"/>.</summary>
     /// <param name="settingsScope">The Settings Scope object that <paramref name="stream"/> will be loaded into.</param>
@@ -62,11 +63,8 @@ public class SourcesManager
 
     internal void AddSource (ConfigLocations location, string source)
     {
-        if (!Sources.TryAdd (location, source))
-        {
-            //Logging.Warning ($"{location} has already been added to Sources.");
-            Sources [location] = source;
-        }
+        // ConcurrentDictionary's AddOrUpdate is thread-safe
+        Sources.AddOrUpdate (location, source, (key, oldValue) => source);
     }
 
 

+ 4 - 4
Terminal.Gui/Drivers/FakeDriver/FakeInputProcessor.cs

@@ -1,4 +1,3 @@
-#nullable disable
 using System.Collections.Concurrent;
 
 namespace Terminal.Gui.Drivers;
@@ -27,17 +26,18 @@ public class FakeInputProcessor : InputProcessorImpl<ConsoleKeyInfo>
     }
 
     /// <inheritdoc />
-    public override void EnqueueMouseEvent (MouseEventArgs mouseEvent)
+    public override void EnqueueMouseEvent (IApplication? app, MouseEventArgs mouseEvent)
     {
         // FakeDriver uses ConsoleKeyInfo as its input record type, which cannot represent mouse events.
 
+        // TODO: Verify this is correct. This didn't check the threadId before.
         // If Application.Invoke is available (running in Application context), defer to next iteration
         // to ensure proper timing - the event is raised after views are laid out.
         // Otherwise (unit tests), raise immediately so tests can verify synchronously.
-        if (Application.MainThreadId is { })
+        if (app is {} && app.MainThreadId != Thread.CurrentThread.ManagedThreadId)
         {
             // Application is running - use Invoke to defer to next iteration
-            ApplicationImpl.Instance.Invoke ((_) => RaiseMouseEvent (mouseEvent));
+            app?.Invoke ((_) => RaiseMouseEvent (mouseEvent));
         }
         else
         {

+ 9 - 6
Terminal.Gui/Drivers/IInputProcessor.cs

@@ -1,5 +1,4 @@
-
-namespace Terminal.Gui.Drivers;
+namespace Terminal.Gui.Drivers;
 
 /// <summary>
 ///     Interface for main loop class that will process the queued input.
@@ -12,7 +11,7 @@ public interface IInputProcessor
     public event EventHandler<string>? AnsiSequenceSwallowed;
 
     /// <summary>
-    /// Gets the name of the driver associated with this input processor.
+    ///     Gets the name of the driver associated with this input processor.
     /// </summary>
     string? DriverName { get; init; }
 
@@ -58,7 +57,8 @@ public interface IInputProcessor
     ///     Called when a key up event has been dequeued. Raises the <see cref="KeyUp"/> event.
     /// </summary>
     /// <remarks>
-    ///     Drivers that do not support key release events will call this method after <see cref="RaiseKeyDownEvent"/> processing
+    ///     Drivers that do not support key release events will call this method after <see cref="RaiseKeyDownEvent"/>
+    ///     processing
     ///     is complete.
     /// </remarks>
     /// <param name="key">The key event data.</param>
@@ -89,7 +89,10 @@ public interface IInputProcessor
     /// <summary>
     ///     Adds a mouse input event to the input queue. For unit tests.
     /// </summary>
+    /// <param name="app">
+    ///     The application instance to use. Used to use Invoke to raise the mouse
+    ///     event in the case where this method is not called on the main thread.
+    /// </param>
     /// <param name="mouseEvent"></param>
-    void EnqueueMouseEvent (MouseEventArgs mouseEvent);
-
+    void EnqueueMouseEvent (IApplication? app,  MouseEventArgs mouseEvent);
 }

+ 1 - 1
Terminal.Gui/Drivers/InputProcessorImpl.cs

@@ -122,7 +122,7 @@ public abstract class InputProcessorImpl<TInputRecord> : IInputProcessor, IDispo
     public event EventHandler<MouseEventArgs>? MouseEvent;
 
     /// <inheritdoc />
-    public virtual void EnqueueMouseEvent (MouseEventArgs mouseEvent)
+    public virtual void EnqueueMouseEvent (IApplication? app, MouseEventArgs mouseEvent)
     {
         // Base implementation: For drivers where TInputRecord cannot represent mouse events
         // (e.g., ConsoleKeyInfo), derived classes should override this method.

+ 10 - 8
Terminal.Gui/Drivers/OutputBase.cs

@@ -90,14 +90,16 @@ public abstract class OutputBase
             }
         }
 
-        foreach (SixelToRender s in Application.Sixel)
-        {
-            if (!string.IsNullOrWhiteSpace (s.SixelData))
-            {
-                SetCursorPositionImpl (s.ScreenPosition.X, s.ScreenPosition.Y);
-                Console.Out.Write (s.SixelData);
-            }
-        }
+        // BUGBUG: The Sixel impl depends on the legacy static Application object
+        // BUGBUG: Disabled for now
+        //foreach (SixelToRender s in  Application.Sixel)
+        //{
+        //    if (!string.IsNullOrWhiteSpace (s.SixelData))
+        //    {
+        //        SetCursorPositionImpl (s.ScreenPosition.X, s.ScreenPosition.Y);
+        //        Console.Out.Write (s.SixelData);
+        //    }
+        //}
 
         SetCursorVisibility (savedVisibility ?? CursorVisibility.Default);
         _cachedCursorVisibility = savedVisibility;

+ 1 - 1
Terminal.Gui/Drivers/WindowsDriver/WindowsInputProcessor.cs

@@ -18,7 +18,7 @@ internal class WindowsInputProcessor : InputProcessorImpl<InputRecord>
     }
 
     /// <inheritdoc />
-    public override void EnqueueMouseEvent (MouseEventArgs mouseEvent)
+    public override void EnqueueMouseEvent (IApplication? app, MouseEventArgs mouseEvent)
     {
         InputQueue.Enqueue (new ()
         {

+ 3 - 2
Terminal.Gui/Drivers/WindowsDriver/WindowsOutput.cs

@@ -149,7 +149,7 @@ internal partial class WindowsOutput : OutputBase, IOutput
             // Force 16 colors if not in virtual terminal mode.
             // BUGBUG: This is bad. It does not work if the app was crated without
             // BUGBUG: Apis.
-            ApplicationImpl.Instance.Force16Colors = true;
+            //ApplicationImpl.Instance.Force16Colors = true;
 
         }
 
@@ -357,7 +357,8 @@ internal partial class WindowsOutput : OutputBase, IOutput
     {
         // BUGBUG: This is bad. It does not work if the app was crated without
         // BUGBUG: Apis.
-        bool force16Colors = ApplicationImpl.Instance.Force16Colors;
+        // bool force16Colors = ApplicationImpl.Instance.Force16Colors;
+        bool force16Colors = false;
 
         if (force16Colors)
         {

+ 6 - 3
Terminal.Gui/FileServices/IFileOperations.cs

@@ -9,28 +9,31 @@ namespace Terminal.Gui.FileServices;
 public interface IFileOperations
 {
     /// <summary>Specifies how to handle file/directory deletion attempts in <see cref="FileDialog"/>.</summary>
+    /// <param name="app"></param>
     /// <param name="toDelete"></param>
     /// <returns><see langword="true"/> if operation was completed or <see langword="false"/> if cancelled</returns>
     /// <remarks>
     ///     Ensure you use a try/catch block with appropriate error handling (e.g. showing a <see cref="MessageBox"/>
     /// </remarks>
-    bool Delete (IEnumerable<IFileSystemInfo> toDelete);
+    bool Delete (IApplication? app, IEnumerable<IFileSystemInfo> toDelete);
 
     /// <summary>Specifies how to handle 'new directory' operation in <see cref="FileDialog"/>.</summary>
+    /// <param name="app"></param>
     /// <param name="fileSystem"></param>
     /// <param name="inDirectory">The parent directory in which the new directory should be created</param>
     /// <returns>The newly created directory or null if cancelled.</returns>
     /// <remarks>
     ///     Ensure you use a try/catch block with appropriate error handling (e.g. showing a <see cref="MessageBox"/>
     /// </remarks>
-    IFileSystemInfo New (IFileSystem fileSystem, IDirectoryInfo inDirectory);
+    IFileSystemInfo New (IApplication? app, IFileSystem fileSystem, IDirectoryInfo inDirectory);
 
     /// <summary>Specifies how to handle file/directory rename attempts in <see cref="FileDialog"/>.</summary>
+    /// <param name="app"></param>
     /// <param name="fileSystem"></param>
     /// <param name="toRename"></param>
     /// <returns>The new name for the file or null if cancelled</returns>
     /// <remarks>
     ///     Ensure you use a try/catch block with appropriate error handling (e.g. showing a <see cref="MessageBox"/>
     /// </remarks>
-    IFileSystemInfo Rename (IFileSystem fileSystem, IFileSystemInfo toRename);
+    IFileSystemInfo Rename (IApplication? app, IFileSystem fileSystem, IFileSystemInfo toRename);
 }

+ 95 - 83
Terminal.Gui/Input/InputBindings.cs

@@ -1,19 +1,15 @@
-namespace Terminal.Gui.Input;
+using System.Collections.Concurrent;
+
+namespace Terminal.Gui.Input;
 
 /// <summary>
 ///     Abstract class for <see cref="KeyBindings"/> and <see cref="MouseBindings"/>.
+///     This class is thread-safe for all public operations.
 /// </summary>
 /// <typeparam name="TEvent">The type of the event (e.g. <see cref="Key"/> or <see cref="MouseFlags"/>).</typeparam>
 /// <typeparam name="TBinding">The binding type (e.g. <see cref="KeyBinding"/>).</typeparam>
-public abstract class InputBindings<TEvent, TBinding> where TBinding : IInputBinding, new () where TEvent : notnull
+public abstract class InputBindings<TEvent, TBinding> where TBinding : IInputBinding, new() where TEvent : notnull
 {
-    /// <summary>
-    ///     The bindings.
-    /// </summary>
-    private readonly Dictionary<TEvent, TBinding> _bindings;
-
-    private readonly Func<Command [], TEvent, TBinding> _constructBinding;
-
     /// <summary>
     ///     Initializes a new instance.
     /// </summary>
@@ -26,11 +22,11 @@ public abstract class InputBindings<TEvent, TBinding> where TBinding : IInputBin
     }
 
     /// <summary>
-    ///     Tests whether <paramref name="eventArgs"/> is valid or not.
+    ///     The bindings.
     /// </summary>
-    /// <param name="eventArgs"></param>
-    /// <returns></returns>
-    public abstract bool IsValid (TEvent eventArgs);
+    private readonly ConcurrentDictionary<TEvent, TBinding> _bindings;
+
+    private readonly Func<Command [], TEvent, TBinding> _constructBinding;
 
     /// <summary>Adds a <typeparamref name="TEvent"/> bound to <typeparamref name="TBinding"/> to the collection.</summary>
     /// <param name="eventArgs"></param>
@@ -42,24 +38,21 @@ public abstract class InputBindings<TEvent, TBinding> where TBinding : IInputBin
             throw new ArgumentException (@"Invalid newEventArgs", nameof (eventArgs));
         }
 
-#pragma warning disable CS8601 // Possible null reference assignment.
-        if (TryGet (eventArgs, out TBinding _))
+        // IMPORTANT: Add a COPY of the eventArgs. This is needed because ConfigurationManager.Apply uses DeepMemberWiseCopy
+        // IMPORTANT: update the memory referenced by the key, and Dictionary uses caching for performance, and thus
+        // IMPORTANT: Apply will update the Dictionary with the new eventArgs, but the old eventArgs will still be in the dictionary.
+        // IMPORTANT: See the ConfigurationManager.Illustrate_DeepMemberWiseCopy_Breaks_Dictionary test for details.
+        if (!_bindings.TryAdd (eventArgs, binding))
         {
             throw new InvalidOperationException (@$"A binding for {eventArgs} exists ({binding}).");
         }
-#pragma warning restore CS8601 // Possible null reference assignment.
-
-        // IMPORTANT: Add a COPY of the eventArgs. This is needed because ConfigurationManager.Apply uses DeepMemberWiseCopy 
-        // IMPORTANT: update the memory referenced by the key, and Dictionary uses caching for performance, and thus 
-        // IMPORTANT: Apply will update the Dictionary with the new eventArgs, but the old eventArgs will still be in the dictionary.
-        // IMPORTANT: See the ConfigurationManager.Illustrate_DeepMemberWiseCopy_Breaks_Dictionary test for details.
-        _bindings.Add (eventArgs, binding);
     }
 
     /// <summary>
     ///     <para>Adds a new <typeparamref name="TEvent"/> that will trigger the commands in <paramref name="commands"/>.</para>
     ///     <para>
-    ///         If the <typeparamref name="TEvent"/> is already bound to a different set of <see cref="Command"/>s it will be rebound
+    ///         If the <typeparamref name="TEvent"/> is already bound to a different set of <see cref="Command"/>s it will be
+    ///         rebound
     ///         <paramref name="commands"/>.
     ///     </para>
     /// </summary>
@@ -77,31 +70,32 @@ public abstract class InputBindings<TEvent, TBinding> where TBinding : IInputBin
             throw new ArgumentException (@"At least one command must be specified", nameof (commands));
         }
 
-        if (TryGet (eventArgs, out TBinding? binding))
+        if (!IsValid (eventArgs))
         {
-            throw new InvalidOperationException (@$"A binding for {eventArgs} exists ({binding}).");
+            throw new ArgumentException (@"Invalid newEventArgs", nameof (eventArgs));
         }
 
-        Add (eventArgs, _constructBinding (commands, eventArgs));
-    }
+        TBinding binding = _constructBinding (commands, eventArgs);
 
-    /// <summary>
-    ///     Gets the bindings.
-    /// </summary>
-    /// <returns></returns>
-    public IEnumerable<KeyValuePair<TEvent, TBinding>> GetBindings () { return _bindings; }
+        if (!_bindings.TryAdd (eventArgs, binding))
+        {
+            throw new InvalidOperationException (@$"A binding for {eventArgs} exists ({binding}).");
+        }
+    }
 
     /// <summary>Removes all <typeparamref name="TEvent"/> objects from the collection.</summary>
     public void Clear () { _bindings.Clear (); }
 
     /// <summary>
-    ///     Removes all bindings that trigger the given command set. Views can have multiple different <typeparamref name="TEvent"/>
+    ///     Removes all bindings that trigger the given command set. Views can have multiple different
+    ///     <typeparamref name="TEvent"/>
     ///     bound to
     ///     the same command sets and this method will clear all of them.
     /// </summary>
     /// <param name="command"></param>
     public void Clear (params Command [] command)
     {
+        // ToArray() creates a snapshot to avoid modification during enumeration
         KeyValuePair<TEvent, TBinding> [] kvps = _bindings
                                                  .Where (kvp => kvp.Value.Commands.SequenceEqual (command))
                                                  .ToArray ();
@@ -125,16 +119,29 @@ public abstract class InputBindings<TEvent, TBinding> where TBinding : IInputBin
         throw new InvalidOperationException ($"{eventArgs} is not bound.");
     }
 
-    /// <summary>Gets the commands bound with the specified <typeparamref name="TEvent"/>.</summary>
-    /// <remarks></remarks>
-    /// <param name="eventArgs">The <typeparamref name="TEvent"/> to check.</param>
-    /// <param name="binding">
-    ///     When this method returns, contains the commands bound with the <typeparamref name="TEvent"/>, if the <typeparamref name="TEvent"/> is
-    ///     not
-    ///     found; otherwise, null. This parameter is passed uninitialized.
-    /// </param>
-    /// <returns><see langword="true"/> if the <typeparamref name="TEvent"/> is bound; otherwise <see langword="false"/>.</returns>
-    public bool TryGet (TEvent eventArgs, out TBinding? binding) { return _bindings.TryGetValue (eventArgs, out binding); }
+    /// <summary>Gets all <typeparamref name="TEvent"/> bound to the set of commands specified by <paramref name="commands"/>.</summary>
+    /// <param name="commands">The set of commands to search.</param>
+    /// <returns>
+    ///     The <typeparamref name="TEvent"/>s bound to the set of commands specified by <paramref name="commands"/>. An empty
+    ///     list if
+    ///     the
+    ///     set of commands was not found.
+    /// </returns>
+    public IEnumerable<TEvent> GetAllFromCommands (params Command [] commands)
+    {
+        // ToList() creates a snapshot to ensure thread-safe enumeration
+        return _bindings.Where (a => a.Value.Commands.SequenceEqual (commands)).Select (a => a.Key).ToList ();
+    }
+
+    /// <summary>
+    ///     Gets the bindings.
+    /// </summary>
+    /// <returns></returns>
+    public IEnumerable<KeyValuePair<TEvent, TBinding>> GetBindings ()
+    {
+        // ConcurrentDictionary provides a snapshot enumeration that is safe for concurrent access
+        return _bindings;
+    }
 
     /// <summary>Gets the array of <see cref="Command"/>s bound to <paramref name="eventArgs"/> if it exists.</summary>
     /// <param name="eventArgs">The <typeparamref name="TEvent"/> to check.</param>
@@ -163,17 +170,16 @@ public abstract class InputBindings<TEvent, TBinding> where TBinding : IInputBin
     /// </returns>
     public TEvent? GetFirstFromCommands (params Command [] commands) { return _bindings.FirstOrDefault (a => a.Value.Commands.SequenceEqual (commands)).Key; }
 
-    /// <summary>Gets all <typeparamref name="TEvent"/> bound to the set of commands specified by <paramref name="commands"/>.</summary>
-    /// <param name="commands">The set of commands to search.</param>
-    /// <returns>
-    ///     The <typeparamref name="TEvent"/>s bound to the set of commands specified by <paramref name="commands"/>. An empty list if
-    ///     the
-    ///     set of commands was not found.
-    /// </returns>
-    public IEnumerable<TEvent> GetAllFromCommands (params Command [] commands)
-    {
-        return _bindings.Where (a => a.Value.Commands.SequenceEqual (commands)).Select (a => a.Key);
-    }
+    /// <summary>
+    ///     Tests whether <paramref name="eventArgs"/> is valid or not.
+    /// </summary>
+    /// <param name="eventArgs"></param>
+    /// <returns></returns>
+    public abstract bool IsValid (TEvent eventArgs);
+
+    /// <summary>Removes a <typeparamref name="TEvent"/> from the collection.</summary>
+    /// <param name="eventArgs"></param>
+    public void Remove (TEvent eventArgs) { _bindings.TryRemove (eventArgs, out _); }
 
     /// <summary>Replaces a <typeparamref name="TEvent"/> combination already bound to a set of <see cref="Command"/>s.</summary>
     /// <remarks></remarks>
@@ -188,15 +194,28 @@ public abstract class InputBindings<TEvent, TBinding> where TBinding : IInputBin
             throw new ArgumentException (@"Invalid newEventArgs", nameof (newEventArgs));
         }
 
-        if (TryGet (oldEventArgs, out TBinding? binding))
-        {
-            Remove (oldEventArgs);
-            Add (newEventArgs, binding!);
-        }
-        else
+        // Thread-safe: Handle the case where oldEventArgs == newEventArgs
+        if (EqualityComparer<TEvent>.Default.Equals (oldEventArgs, newEventArgs))
         {
-            Add (newEventArgs, binding!);
+            // Same key - nothing to do, binding stays as-is
+            return;
         }
+
+        // Thread-safe: Get the binding from oldEventArgs, or create default if it doesn't exist
+        // This is atomic - either gets existing or adds new
+        TBinding binding = _bindings.GetOrAdd (oldEventArgs, _ => new TBinding ());
+
+        // Thread-safe: Atomically add/update newEventArgs with the binding from oldEventArgs
+        // The updateValueFactory is only called if the key already exists, ensuring we don't
+        // accidentally overwrite a binding that was added by another thread
+        _bindings.AddOrUpdate (
+            newEventArgs,
+            binding, // Add this binding if newEventArgs doesn't exist
+            (_, _) => binding);
+
+        // Thread-safe: Remove oldEventArgs only after newEventArgs has been set
+        // This ensures we don't lose the binding if another thread is reading it
+        _bindings.TryRemove (oldEventArgs, out _);
     }
 
     /// <summary>Replaces the commands already bound to a combination of <typeparamref name="TEvent"/>.</summary>
@@ -209,28 +228,21 @@ public abstract class InputBindings<TEvent, TBinding> where TBinding : IInputBin
     /// <param name="newCommands">The set of commands to replace the old ones with.</param>
     public void ReplaceCommands (TEvent eventArgs, params Command [] newCommands)
     {
-#pragma warning disable CS8601 // Possible null reference assignment.
-        if (TryGet (eventArgs, out TBinding _))
-        {
-            Remove (eventArgs);
-            Add (eventArgs, newCommands);
-        }
-        else
-        {
-            Add (eventArgs, newCommands);
-        }
-#pragma warning restore CS8601 // Possible null reference assignment.
-    }
+        TBinding newBinding = _constructBinding (newCommands, eventArgs);
 
-    /// <summary>Removes a <typeparamref name="TEvent"/> from the collection.</summary>
-    /// <param name="eventArgs"></param>
-    public void Remove (TEvent eventArgs)
-    {
-        if (!TryGet (eventArgs, out _))
-        {
-            return;
-        }
-
-        _bindings.Remove (eventArgs);
+        // Thread-safe: Add or update atomically
+        _bindings.AddOrUpdate (eventArgs, newBinding, (_, _) => newBinding);
     }
+
+    /// <summary>Gets the commands bound with the specified <typeparamref name="TEvent"/>.</summary>
+    /// <remarks></remarks>
+    /// <param name="eventArgs">The <typeparamref name="TEvent"/> to check.</param>
+    /// <param name="binding">
+    ///     When this method returns, contains the commands bound with the <typeparamref name="TEvent"/>, if the
+    ///     <typeparamref name="TEvent"/> is
+    ///     not
+    ///     found; otherwise, null. This parameter is passed uninitialized.
+    /// </param>
+    /// <returns><see langword="true"/> if the <typeparamref name="TEvent"/> is bound; otherwise <see langword="false"/>.</returns>
+    public bool TryGet (TEvent eventArgs, out TBinding? binding) { return _bindings.TryGetValue (eventArgs, out binding); }
 }

+ 1 - 1
Terminal.Gui/ViewBase/Runnable.cs

@@ -92,7 +92,7 @@ public class Runnable<TResult> : View, IRunnable<TResult>
     ///         // Or check if user wants to save first
     ///         if (HasUnsavedChanges ())
     ///         {
-    ///             int result = MessageBox.Query ("Save?", "Save changes?", "Yes", "No", "Cancel");
+    ///             int result = MessageBox.Query (App, "Save?", "Save changes?", "Yes", "No", "Cancel");
     ///             if (result == 2) return true;  // Cancel stopping
     ///             if (result == 0) Save ();
     ///         }

+ 2 - 1
Terminal.Gui/ViewBase/RunnableWrapper.cs

@@ -8,7 +8,8 @@ namespace Terminal.Gui.ViewBase;
 /// <typeparam name="TResult">The type of result data returned when the session completes.</typeparam>
 /// <remarks>
 ///     <para>
-///         This class enables any View to be run as a blocking session with <see cref="IApplication.Run"/>
+///         This class enables any View to be run as a blocking session with
+///         <see cref="IApplication.Run(Func{Exception, bool}, string)"/>
 ///         without requiring the View to implement <see cref="IRunnable{TResult}"/> or derive from
 ///         <see cref="Runnable{TResult}"/>.
 ///     </para>

+ 1 - 0
Terminal.Gui/ViewBase/View.Diagnostics.cs

@@ -2,6 +2,7 @@
 
 public partial class View
 {
+    // TODO: Make this a configuration property
     /// <summary>Gets or sets whether diagnostic information will be drawn. This is a bit-field of <see cref="ViewDiagnosticFlags"/>.e <see cref="View"/> diagnostics.</summary>
     /// <remarks>
     /// <para>

+ 2 - 2
Terminal.Gui/ViewBase/View.Drawing.Attribute.cs

@@ -104,7 +104,7 @@ public partial class View
 
     /// <summary>
     ///     Selects the specified Attribute
-    ///     as the Attribute to use for subsequent calls to <see cref="AddRune(System.Text.Rune)"/> and <see cref="AddStr"/>.
+    ///     as the Attribute to use for subsequent calls to <see cref="AddRune(System.Text.Rune)"/> and <see cref="AddStr(string)"/>.
     /// </summary>
     /// <param name="attribute">THe Attribute to set.</param>
     /// <returns>The previously set Attribute.</returns>
@@ -112,7 +112,7 @@ public partial class View
 
     /// <summary>
     ///     Selects the Attribute associated with the specified <see cref="VisualRole"/>
-    ///     as the Attribute to use for subsequent calls to <see cref="AddRune(System.Text.Rune)"/> and <see cref="AddStr"/>.
+    ///     as the Attribute to use for subsequent calls to <see cref="AddRune(System.Text.Rune)"/> and <see cref="AddStr(string)"/>.
     ///     <para>
     ///         Calls <see cref="GetAttributeForRole"/> to get the Attribute associated with the specified role, which will
     ///         raise <see cref="OnGettingAttributeForRole"/>/<see cref="GettingAttributeForRole"/>.

+ 13 - 3
Terminal.Gui/Views/Button.cs

@@ -1,4 +1,3 @@
-
 #nullable disable
 namespace Terminal.Gui.Views;
 
@@ -24,6 +23,9 @@ namespace Terminal.Gui.Views;
 /// </remarks>
 public class Button : View, IDesignable
 {
+    private static ShadowStyle _defaultShadow = ShadowStyle.Opaque; // Resources/config.json overrides
+    private static MouseState _defaultHighlightStates = MouseState.In | MouseState.Pressed | MouseState.PressedOutside; // Resources/config.json overrides
+
     private readonly Rune _leftBracket;
     private readonly Rune _leftDefault;
     private readonly Rune _rightBracket;
@@ -34,13 +36,21 @@ public class Button : View, IDesignable
     ///     Gets or sets whether <see cref="Button"/>s are shown with a shadow effect by default.
     /// </summary>
     [ConfigurationProperty (Scope = typeof (ThemeScope))]
-    public static ShadowStyle DefaultShadow { get; set; } = ShadowStyle.Opaque;
+    public static ShadowStyle DefaultShadow
+    {
+        get => _defaultShadow;
+        set => _defaultShadow = value;
+    }
 
     /// <summary>
     ///     Gets or sets the default Highlight Style.
     /// </summary>
     [ConfigurationProperty (Scope = typeof (ThemeScope))]
-    public static MouseState DefaultHighlightStates { get; set; } = MouseState.In | MouseState.Pressed | MouseState.PressedOutside;
+    public static MouseState DefaultHighlightStates
+    {
+        get => _defaultHighlightStates;
+        set => _defaultHighlightStates = value;
+    }
 
     /// <summary>Initializes a new instance of <see cref="Button"/>.</summary>
     public Button ()

+ 7 - 7
Terminal.Gui/Views/CharMap/CharMap.cs

@@ -281,8 +281,8 @@ public class CharMap : View, IDesignable
         }
     }
 
-    private void CopyCodePoint () { Clipboard.Contents = $"U+{SelectedCodePoint:x5}"; }
-    private void CopyGlyph () { Clipboard.Contents = $"{new Rune (SelectedCodePoint)}"; }
+    private void CopyCodePoint () { App?.Clipboard?.SetClipboardData($"U+{SelectedCodePoint:x5}"); }
+    private void CopyGlyph () { App?.Clipboard?.SetClipboardData($"{new Rune (SelectedCodePoint)}"); }
 
     private bool? Move (ICommandContext? commandContext, int cpOffset)
     {
@@ -335,7 +335,7 @@ public class CharMap : View, IDesignable
     [RequiresDynamicCode ("AOT")]
     private void ShowDetails ()
     {
-        if (!Application.Initialized)
+        if (App is not { Initialized: true })
         {
             // Some unit tests invoke Accept without Init
             return;
@@ -380,15 +380,15 @@ public class CharMap : View, IDesignable
                                    try
                                    {
                                        decResponse = await client.GetCodepointDec (SelectedCodePoint).ConfigureAwait (false);
-                                       Application.Invoke ((_) => waitIndicator.RequestStop ());
+                                       App?.Invoke ((_) => (s as Dialog)?.RequestStop ());
                                    }
                                    catch (HttpRequestException e)
                                    {
                                        getCodePointError = errorLabel.Text = e.Message;
-                                       Application.Invoke ((_) => waitIndicator.RequestStop ());
+                                       App?.Invoke ((_) => (s as Dialog)?.RequestStop ());
                                    }
                                };
-        Application.Run (waitIndicator);
+        App?.Run (waitIndicator);
         waitIndicator.Dispose ();
 
         var name = string.Empty;
@@ -521,7 +521,7 @@ public class CharMap : View, IDesignable
 
         dlg.Add (json);
 
-        Application.Run (dlg);
+        App?.Run (dlg);
         dlg.Dispose ();
     }
 

+ 7 - 3
Terminal.Gui/Views/CheckBox.cs

@@ -1,5 +1,3 @@
-
-
 namespace Terminal.Gui.Views;
 
 /// <summary>Shows a checkbox that can be cycled between two or three states.</summary>
@@ -10,11 +8,17 @@ namespace Terminal.Gui.Views;
 /// </remarks>
 public class CheckBox : View
 {
+    private static MouseState _defaultHighlightStates = MouseState.PressedOutside | MouseState.Pressed | MouseState.In; // Resources/config.json overrides
+
     /// <summary>
     ///     Gets or sets the default Highlight Style.
     /// </summary>
     [ConfigurationProperty (Scope = typeof (ThemeScope))]
-    public static MouseState DefaultHighlightStates { get; set; } = MouseState.PressedOutside | MouseState.Pressed | MouseState.In;
+    public static MouseState DefaultHighlightStates
+    {
+        get => _defaultHighlightStates;
+        set => _defaultHighlightStates = value;
+    }
 
     /// <summary>
     ///     Initializes a new instance of <see cref="CheckBox"/>.

+ 35 - 4
Terminal.Gui/Views/CollectionNavigation/CollectionNavigator.cs

@@ -1,5 +1,5 @@
 #nullable disable
-using System.Collections;
+using System.Collections;
 
 namespace Terminal.Gui.Views;
 
@@ -7,6 +7,9 @@ namespace Terminal.Gui.Views;
 /// <remarks>This implementation is based on a static <see cref="Collection"/> of objects.</remarks>
 internal class CollectionNavigator : CollectionNavigatorBase, IListCollectionNavigator
 {
+    private readonly object _collectionLock = new ();
+    private IList _collection;
+
     /// <summary>Constructs a new CollectionNavigator.</summary>
     public CollectionNavigator () { }
 
@@ -15,11 +18,39 @@ internal class CollectionNavigator : CollectionNavigatorBase, IListCollectionNav
     public CollectionNavigator (IList collection) { Collection = collection; }
 
     /// <inheritdoc/>
-    public IList Collection { get; set; }
+    public IList Collection
+    {
+        get
+        {
+            lock (_collectionLock)
+            {
+                return _collection;
+            }
+        }
+        set
+        {
+            lock (_collectionLock)
+            {
+                _collection = value;
+            }
+        }
+    }
 
     /// <inheritdoc/>
-    protected override object ElementAt (int idx) { return Collection [idx]; }
+    protected override object ElementAt (int idx)
+    {
+        lock (_collectionLock)
+        {
+            return Collection [idx];
+        }
+    }
 
     /// <inheritdoc/>
-    protected override int GetCollectionLength () { return Collection.Count; }
+    protected override int GetCollectionLength ()
+    {
+        lock (_collectionLock)
+        {
+            return Collection.Count;
+        }
+    }
 }

+ 38 - 10
Terminal.Gui/Views/CollectionNavigation/CollectionNavigatorBase.cs

@@ -1,10 +1,9 @@
-
-
 namespace Terminal.Gui.Views;
 
 /// <inheritdoc/>
 internal abstract class CollectionNavigatorBase : ICollectionNavigator
 {
+    private readonly object _lock = new ();
     private DateTime _lastKeystroke = DateTime.Now;
     private string _searchString = "";
 
@@ -14,10 +13,20 @@ internal abstract class CollectionNavigatorBase : ICollectionNavigator
     /// <inheritdoc/>
     public string SearchString
     {
-        get => _searchString;
+        get
+        {
+            lock (_lock)
+            {
+                return _searchString;
+            }
+        }
         private set
         {
-            _searchString = value;
+            lock (_lock)
+            {
+                _searchString = value;
+            }
+
             OnSearchStringChanged (new (value));
         }
     }
@@ -40,15 +49,22 @@ internal abstract class CollectionNavigatorBase : ICollectionNavigator
             // but if we find none then we must fallback on cycling
             // d instead and discard the candidate state
             var candidateState = "";
-            TimeSpan elapsedTime = DateTime.Now - _lastKeystroke;
+            TimeSpan elapsedTime;
+            string currentSearchString;
+
+            lock (_lock)
+            {
+                elapsedTime = DateTime.Now - _lastKeystroke;
+                currentSearchString = _searchString;
+            }
 
             Logging.Debug ($"CollectionNavigator began processing '{keyStruck}', it has been {elapsedTime} since last keystroke");
 
             // is it a second or third (etc) keystroke within a short time
-            if (SearchString.Length > 0 && elapsedTime < TimeSpan.FromMilliseconds (TypingDelay))
+            if (currentSearchString.Length > 0 && elapsedTime < TimeSpan.FromMilliseconds (TypingDelay))
             {
                 // "dd" is a candidate
-                candidateState = SearchString + keyStruck;
+                candidateState = currentSearchString + keyStruck;
                 Logging.Debug ($"Appending, search is now for '{candidateState}'");
             }
             else
@@ -72,7 +88,11 @@ internal abstract class CollectionNavigatorBase : ICollectionNavigator
             if (idxCandidate is { })
             {
                 // found "dd" so candidate search string is accepted
-                _lastKeystroke = DateTime.Now;
+                lock (_lock)
+                {
+                    _lastKeystroke = DateTime.Now;
+                }
+
                 SearchString = candidateState;
 
                 Logging.Debug ($"Found collection item that matched search:{idxCandidate}");
@@ -82,7 +102,11 @@ internal abstract class CollectionNavigatorBase : ICollectionNavigator
 
             //// nothing matches "dd" so discard it as a candidate
             //// and just cycle "d" instead
-            _lastKeystroke = DateTime.Now;
+            lock (_lock)
+            {
+                _lastKeystroke = DateTime.Now;
+            }
+
             idxCandidate = GetNextMatchingItem (currentIndex, candidateState);
 
             Logging.Debug ($"CollectionNavigator searching (any match) matched:{idxCandidate}");
@@ -206,6 +230,10 @@ internal abstract class CollectionNavigatorBase : ICollectionNavigator
     private void ClearSearchString ()
     {
         SearchString = "";
-        _lastKeystroke = DateTime.Now;
+
+        lock (_lock)
+        {
+            _lastKeystroke = DateTime.Now;
+        }
     }
 }

+ 2 - 2
Terminal.Gui/Views/Color/ColorPicker.Prompt.cs

@@ -37,7 +37,7 @@ public partial class ColorPicker
                         {
                             accept = true;
                             e.Handled = true;
-                            Application.RequestStop ();
+                            (s as View)?.App?.RequestStop ();
                         };
 
         var btnCancel = new Button
@@ -51,7 +51,7 @@ public partial class ColorPicker
         btnCancel.Accepting += (s, e) =>
                             {
                                 e.Handled = true;
-                                Application.RequestStop ();
+                                (s as View)?.App ?.RequestStop ();
                             };
 
         d.Add (btnOk);

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

@@ -1,4 +1,3 @@
-
 namespace Terminal.Gui.Views;
 
 /// <summary>
@@ -23,6 +22,13 @@ namespace Terminal.Gui.Views;
 /// </remarks>
 public class Dialog : Window, IRunnable<int?>
 {
+    private static LineStyle _defaultBorderStyle = LineStyle.Heavy; // Resources/config.json overrides
+    private static Alignment _defaultButtonAlignment = Alignment.End; // Resources/config.json overrides
+    private static AlignmentModes _defaultButtonAlignmentModes = AlignmentModes.StartToEnd | AlignmentModes.AddSpaceBetweenItems; // Resources/config.json overrides
+    private static int _defaultMinimumHeight = 80; // Resources/config.json overrides
+    private static int _defaultMinimumWidth = 80; // Resources/config.json overrides
+    private static ShadowStyle _defaultShadow = ShadowStyle.Transparent; // Resources/config.json overrides
+
     /// <summary>
     ///     Initializes a new instance of the <see cref="Dialog"/> class with no <see cref="Button"/>s.
     /// </summary>
@@ -136,37 +142,61 @@ public class Dialog : Window, IRunnable<int?>
     /// </summary>
 
     [ConfigurationProperty (Scope = typeof (ThemeScope))]
-    public new static LineStyle DefaultBorderStyle { get; set; } = LineStyle.Heavy;
+    public new static LineStyle DefaultBorderStyle
+    {
+        get => _defaultBorderStyle;
+        set => _defaultBorderStyle = value;
+    }
 
     /// <summary>The default <see cref="Alignment"/> for <see cref="Dialog"/>.</summary>
     /// <remarks>This property can be set in a Theme.</remarks>
     [ConfigurationProperty (Scope = typeof (ThemeScope))]
-    public static Alignment DefaultButtonAlignment { get; set; } = Alignment.End;
+    public static Alignment DefaultButtonAlignment
+    {
+        get => _defaultButtonAlignment;
+        set => _defaultButtonAlignment = value;
+    }
 
     /// <summary>The default <see cref="AlignmentModes"/> for <see cref="Dialog"/>.</summary>
     /// <remarks>This property can be set in a Theme.</remarks>
     [ConfigurationProperty (Scope = typeof (ThemeScope))]
-    public static AlignmentModes DefaultButtonAlignmentModes { get; set; } = AlignmentModes.StartToEnd | AlignmentModes.AddSpaceBetweenItems;
+    public static AlignmentModes DefaultButtonAlignmentModes
+    {
+        get => _defaultButtonAlignmentModes;
+        set => _defaultButtonAlignmentModes = value;
+    }
 
     /// <summary>
     ///     Defines the default minimum Dialog height, as a percentage of the container width. Can be configured via
     ///     <see cref="ConfigurationManager"/>.
     /// </summary>
     [ConfigurationProperty (Scope = typeof (ThemeScope))]
-    public static int DefaultMinimumHeight { get; set; } = 80;
+    public static int DefaultMinimumHeight
+    {
+        get => _defaultMinimumHeight;
+        set => _defaultMinimumHeight = value;
+    }
 
     /// <summary>
     ///     Defines the default minimum Dialog width, as a percentage of the container width. Can be configured via
     ///     <see cref="ConfigurationManager"/>.
     /// </summary>
     [ConfigurationProperty (Scope = typeof (ThemeScope))]
-    public static int DefaultMinimumWidth { get; set; } = 80;
+    public static int DefaultMinimumWidth
+    {
+        get => _defaultMinimumWidth;
+        set => _defaultMinimumWidth = value;
+    }
 
     /// <summary>
     ///     Gets or sets whether all <see cref="Window"/>s are shown with a shadow effect by default.
     /// </summary>
     [ConfigurationProperty (Scope = typeof (ThemeScope))]
-    public new static ShadowStyle DefaultShadow { get; set; } = ShadowStyle.Transparent;
+    public new static ShadowStyle DefaultShadow
+    {
+        get => _defaultShadow;
+        set => _defaultShadow = value;
+    }
 
 
     // Dialogs are Modal and Focus is indicated by their Border. The following code ensures the

+ 9 - 9
Terminal.Gui/Views/FileDialogs/DefaultFileOperations.cs

@@ -7,7 +7,7 @@ namespace Terminal.Gui.Views;
 public class DefaultFileOperations : IFileOperations
 {
     /// <inheritdoc/>
-    public bool Delete (IEnumerable<IFileSystemInfo> toDelete)
+    public bool Delete (IApplication app, IEnumerable<IFileSystemInfo> toDelete)
     {
         // Default implementation does not allow deleting multiple files
         if (toDelete.Count () != 1)
@@ -18,7 +18,7 @@ public class DefaultFileOperations : IFileOperations
         IFileSystemInfo d = toDelete.Single ();
         string adjective = d.Name;
 
-        int result = MessageBox.Query (
+        int? result = MessageBox.Query (app,
                                        string.Format (Strings.fdDeleteTitle, adjective),
                                        string.Format (Strings.fdDeleteBody, adjective),
                                        Strings.btnYes,
@@ -43,14 +43,14 @@ public class DefaultFileOperations : IFileOperations
         }
         catch (Exception ex)
         {
-            MessageBox.ErrorQuery (Strings.fdDeleteFailedTitle, ex.Message, Strings.btnOk);
+            MessageBox.ErrorQuery (app, Strings.fdDeleteFailedTitle, ex.Message, Strings.btnOk);
         }
 
         return false;
     }
 
     /// <inheritdoc/>
-    public IFileSystemInfo Rename (IFileSystem fileSystem, IFileSystemInfo toRename)
+    public IFileSystemInfo Rename (IApplication app, IFileSystem fileSystem, IFileSystemInfo toRename)
     {
         // Don't allow renaming C: or D: or / (on linux) etc
         if (toRename is IDirectoryInfo dir && dir.Parent is null)
@@ -95,7 +95,7 @@ public class DefaultFileOperations : IFileOperations
                 }
                 catch (Exception ex)
                 {
-                    MessageBox.ErrorQuery (Strings.fdRenameFailedTitle, ex.Message, "Ok");
+                    MessageBox.ErrorQuery (app, Strings.fdRenameFailedTitle, ex.Message, "Ok");
                 }
             }
         }
@@ -104,7 +104,7 @@ public class DefaultFileOperations : IFileOperations
     }
 
     /// <inheritdoc/>
-    public IFileSystemInfo New (IFileSystem fileSystem, IDirectoryInfo inDirectory)
+    public IFileSystemInfo New (IApplication app, IFileSystem fileSystem, IDirectoryInfo inDirectory)
     {
         if (Prompt (Strings.fdNewTitle, "", out string named))
         {
@@ -122,7 +122,7 @@ public class DefaultFileOperations : IFileOperations
                 }
                 catch (Exception ex)
                 {
-                    MessageBox.ErrorQuery (Strings.fdNewFailed, ex.Message, "Ok");
+                    MessageBox.ErrorQuery (app, Strings.fdNewFailed, ex.Message, "Ok");
                 }
             }
         }
@@ -138,7 +138,7 @@ public class DefaultFileOperations : IFileOperations
         btnOk.Accepting += (s, e) =>
                          {
                              confirm = true;
-                             Application.RequestStop ();
+                             (s as View)?.App?.RequestStop ();
                              // When Accepting is handled, set e.Handled to true to prevent further processing.
                              e.Handled = true;
                          };
@@ -147,7 +147,7 @@ public class DefaultFileOperations : IFileOperations
         btnCancel.Accepting += (s, e) =>
                              {
                                  confirm = false;
-                                 Application.RequestStop ();
+                                 (s as View)?.App?.RequestStop ();
                                  // When Accepting is handled, set e.Handled to true to prevent further processing.
                                  e.Handled = true;
                              };

+ 10 - 10
Terminal.Gui/Views/FileDialogs/FileDialog.cs

@@ -108,7 +108,7 @@ public class FileDialog : Dialog, IDesignable
 
                                     if (Modal)
                                     {
-                                        Application.RequestStop ();
+                                        (s as View)?.App?.RequestStop ();
                                     }
                                 };
 
@@ -468,7 +468,6 @@ public class FileDialog : Dialog, IDesignable
         Style.IconProvider.IsOpenGetter = _treeView.IsExpanded;
 
         _treeView.AddObjects (_treeRoots.Keys);
-#if MENU_V1
 
         // if filtering on file type is configured then create the ComboBox and establish
         // initial filtering by extension(s)
@@ -479,6 +478,7 @@ public class FileDialog : Dialog, IDesignable
             // Fiddle factor
             int width = AllowedTypes.Max (a => a.ToString ()!.Length) + 6;
 
+#if MENU_V1
             _allowedTypeMenu = new (
                                     "<placeholder>",
                                     _allowedTypeMenuItems = AllowedTypes.Select (
@@ -512,8 +512,8 @@ public class FileDialog : Dialog, IDesignable
                                                   };
 
             Add (_allowedTypeMenuBar);
-        }
 #endif
+        }
 
         // if no path has been provided
         if (_tbPath.Text.Length <= 0)
@@ -849,7 +849,7 @@ public class FileDialog : Dialog, IDesignable
     {
         IFileSystemInfo [] toDelete = GetFocusedFiles ()!;
 
-        if (FileOperationsHandler.Delete (toDelete))
+        if (FileOperationsHandler.Delete (App, toDelete))
         {
             RefreshState ();
         }
@@ -879,7 +879,7 @@ public class FileDialog : Dialog, IDesignable
 
         if (Modal)
         {
-            Application.RequestStop ();
+            App?.RequestStop ();
         }
     }
 
@@ -1039,7 +1039,7 @@ public class FileDialog : Dialog, IDesignable
     private void New ()
     {
         {
-            IFileSystemInfo created = FileOperationsHandler.New (_fileSystem!, State!.Directory);
+            IFileSystemInfo created = FileOperationsHandler.New (App, _fileSystem!, State!.Directory);
 
             if (created is { })
             {
@@ -1174,13 +1174,13 @@ public class FileDialog : Dialog, IDesignable
         PushState (State, false, false, false);
     }
 
-    private void Rename ()
+    private void Rename (IApplication? app)
     {
         IFileSystemInfo [] toRename = GetFocusedFiles ()!;
 
         if (toRename?.Length == 1)
         {
-            IFileSystemInfo newNamed = FileOperationsHandler.Rename (_fileSystem!, toRename.Single ());
+            IFileSystemInfo newNamed = FileOperationsHandler.Rename (app, _fileSystem!, toRename.Single ());
 
             if (newNamed is { })
             {
@@ -1230,7 +1230,7 @@ public class FileDialog : Dialog, IDesignable
         PopoverMenu? contextMenu = new (
                                         [
                                             new (Strings.fdCtxNew, string.Empty, New),
-                                            new (Strings.fdCtxRename, string.Empty, Rename),
+                                            new (Strings.fdCtxRename, string.Empty, () => Rename (App)),
                                             new (Strings.fdCtxDelete, string.Empty, Delete)
                                         ]);
 
@@ -1327,7 +1327,7 @@ public class FileDialog : Dialog, IDesignable
 
         if (keyEvent.KeyCode == (KeyCode.CtrlMask | KeyCode.R))
         {
-            Rename ();
+            Rename (App);
 
             return true;
         }

+ 9 - 5
Terminal.Gui/Views/FrameView.cs

@@ -1,5 +1,3 @@
-
-
 namespace Terminal.Gui.Views;
 
 // TODO: FrameView is mis-named, really. It's far more about it being a TabGroup than a frame. 
@@ -19,6 +17,8 @@ namespace Terminal.Gui.Views;
 /// <seealso cref="Window"/>
 public class FrameView : View
 {
+    private static LineStyle _defaultBorderStyle = LineStyle.Rounded; // Resources/config.json overrides
+
     /// <summary>
     ///     Initializes a new instance of the <see cref="FrameView"/> class.
     ///     layout.
@@ -31,13 +31,17 @@ public class FrameView : View
     }
 
     /// <summary>
-    ///     The default <see cref="LineStyle"/> for <see cref="FrameView"/>'s border. The default is
-    ///     <see cref="LineStyle.Single"/>.
+    ///     Defines the default border styling for <see cref="FrameView"/>. Can be configured via
+    ///     <see cref="ConfigurationManager"/>.
     /// </summary>
     /// <remarks>
     ///     This property can be set in a Theme to change the default <see cref="LineStyle"/> for all
     ///     <see cref="FrameView"/>s.
     /// </remarks>
     [ConfigurationProperty (Scope = typeof (ThemeScope))]
-    public static LineStyle DefaultBorderStyle { get; set; } = LineStyle.Rounded;
+    public static LineStyle DefaultBorderStyle
+    {
+        get => _defaultBorderStyle;
+        set => _defaultBorderStyle = value;
+    }
 }

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

@@ -8,7 +8,6 @@ public class GraphView : View, IDesignable
     /// <summary>Creates a new graph with a 1 to 1 graph space with absolute layout.</summary>
     public GraphView ()
     {
-        App = ApplicationImpl.Instance;
         CanFocus = true;
 
         AxisX = new ();

+ 4 - 4
Terminal.Gui/Views/Menu/MenuBar.cs

@@ -591,7 +591,7 @@ public class MenuBar : Menu, IDesignable
                                                            {
                                                                Title = "_File Settings...",
                                                                HelpText = "More file settings",
-                                                               Action = () => MessageBox.Query (
+                                                               Action = () => MessageBox.Query (App,
                                                                                                 "File Settings",
                                                                                                 "This is the File Settings Dialog\n",
                                                                                                 "_Ok",
@@ -665,12 +665,12 @@ public class MenuBar : Menu, IDesignable
                                     new MenuItem
                                     {
                                         Title = "_Online Help...",
-                                        Action = () => MessageBox.Query ("Online Help", "https://gui-cs.github.io/Terminal.Gui", "Ok")
+                                        Action = () => MessageBox.Query (App, "Online Help", "https://gui-cs.github.io/Terminal.Gui", "Ok")
                                     },
                                     new MenuItem
                                     {
                                         Title = "About...",
-                                        Action = () => MessageBox.Query ("About", "Something About Mary.", "Ok")
+                                        Action = () => MessageBox.Query (App, "About", "Something About Mary.", "Ok")
                                     }
                                 ]
                                )
@@ -734,7 +734,7 @@ public class MenuBar : Menu, IDesignable
                 {
                     Title = "_Deeper Detail",
                     Text = "Deeper Detail",
-                    Action = () => { MessageBox.Query ("Deeper Detail", "Lots of details", "_Ok"); }
+                    Action = () => { MessageBox.Query (App, "Deeper Detail", "Lots of details", "_Ok"); }
                 };
 
                 var belowLineDetail = new MenuItem

+ 412 - 178
Terminal.Gui/Views/MessageBox.cs

@@ -1,59 +1,109 @@
-#nullable disable
-
 namespace Terminal.Gui.Views;
 
 /// <summary>
-///     MessageBox displays a modal message to the user, with a title, a message and a series of options that the user
-///     can choose from.
+///     Displays a modal message box with a title, message, and buttons. Returns the index of the selected button,
+///     or <see langword="null"/> if the user cancels with <see cref="Application.QuitKey"/>.
 /// </summary>
-/// <para>
-///     The difference between the <see cref="Query(string, string, string[])"/> and
-///     <see cref="ErrorQuery(string, string, string[])"/> method is the default set of colors used for the message box.
-/// </para>
-/// <para>
-///     The following example pops up a <see cref="MessageBox"/> with the specified title and text, plus two
-///     <see cref="Button"/>s. The value -1 is returned when the user cancels the <see cref="MessageBox"/> by pressing the
-///     ESC key.
-/// </para>
-/// <example>
-///     <code lang="c#">
-/// var n = MessageBox.Query ("Quit Demo", "Are you sure you want to quit this demo?", "Yes", "No");
-/// if (n == 0)
-///    quit = true;
-/// else
-///    quit = false;
-/// </code>
-/// </example>
+/// <remarks>
+///     <para>
+///         MessageBox provides static methods for displaying modal dialogs with customizable buttons and messages.
+///         All methods return <see langword="int?"/> where the value is the 0-based index of the button pressed,
+///         or <see langword="null"/> if the user pressed <see cref="Application.QuitKey"/> (typically Esc).
+///     </para>
+///     <para>
+///         <see cref="Query(IApplication?, string, string, string[])"/> uses the default Dialog color scheme.
+///         <see cref="ErrorQuery(IApplication?, string, string, string[])"/> uses the Error color scheme.
+///     </para>
+///     <para>
+///         <b>Important:</b> All MessageBox methods require an <see cref="IApplication"/> instance to be passed.
+///         This enables proper modal dialog management and respects the application's lifecycle. Pass your
+///         application instance (from <see cref="Application.Create()"/>) or use the legacy
+///         <see cref="ApplicationImpl.Instance"/> if using the static Application pattern.
+///     </para>
+///     <para>
+///         Example using instance-based pattern:
+///         <code>
+///     IApplication app = Application.Create();
+///     app.Init();
+///     
+///     int? result = MessageBox.Query(app, "Quit Demo", "Are you sure you want to quit?", "Yes", "No");
+///     if (result == 0) // User clicked "Yes"
+///         app.RequestStop();
+///     else if (result == null) // User pressed Esc
+///         // Handle cancellation
+///         
+///     app.Shutdown();
+///     </code>
+///     </para>
+///     <para>
+///         Example using legacy static pattern:
+///         <code>
+///     Application.Init();
+///     
+///     int? result = MessageBox.Query(ApplicationImpl.Instance, "Quit Demo", "Are you sure?", "Yes", "No");
+///     if (result == 0) // User clicked "Yes"
+///         Application.RequestStop();
+///     
+///     Application.Shutdown();
+///     </code>
+///     </para>
+///     <para>
+///         The <see cref="Clicked"/> property provides a global variable alternative for web-based consoles
+///         without SynchronizationContext. However, using the return value is preferred as it's more thread-safe
+///         and follows modern async patterns.
+///     </para>
+/// </remarks>
 public static class MessageBox
 {
+    private static LineStyle _defaultBorderStyle = LineStyle.Heavy; // Resources/config.json overrides
+    private static Alignment _defaultButtonAlignment = Alignment.Center; // Resources/config.json overrides
+    private static int _defaultMinimumWidth = 0; // Resources/config.json overrides
+    private static int _defaultMinimumHeight = 0; // Resources/config.json overrides
+
     /// <summary>
     ///     Defines the default border styling for <see cref="MessageBox"/>. Can be configured via
     ///     <see cref="ConfigurationManager"/>.
     /// </summary>
     [ConfigurationProperty (Scope = typeof (ThemeScope))]
-    public static LineStyle DefaultBorderStyle { get; set; } = LineStyle.Heavy;
+    public static LineStyle DefaultBorderStyle
+    {
+        get => _defaultBorderStyle;
+        set => _defaultBorderStyle = value;
+    }
 
     /// <summary>The default <see cref="Alignment"/> for <see cref="Dialog"/>.</summary>
     /// <remarks>This property can be set in a Theme.</remarks>
     [ConfigurationProperty (Scope = typeof (ThemeScope))]
-    public static Alignment DefaultButtonAlignment { get; set; } = Alignment.Center;
+    public static Alignment DefaultButtonAlignment
+    {
+        get => _defaultButtonAlignment;
+        set => _defaultButtonAlignment = value;
+    }
 
     /// <summary>
     ///     Defines the default minimum MessageBox width, as a percentage of the screen width. Can be configured via
     ///     <see cref="ConfigurationManager"/>.
     /// </summary>
     [ConfigurationProperty (Scope = typeof (ThemeScope))]
-    public static int DefaultMinimumWidth { get; set; } = 0;
+    public static int DefaultMinimumWidth
+    {
+        get => _defaultMinimumWidth;
+        set => _defaultMinimumWidth = value;
+    }
 
     /// <summary>
     ///     Defines the default minimum Dialog height, as a percentage of the screen width. Can be configured via
     ///     <see cref="ConfigurationManager"/>.
     /// </summary>
     [ConfigurationProperty (Scope = typeof (ThemeScope))]
-    public static int DefaultMinimumHeight { get; set; } = 0;
+    public static int DefaultMinimumHeight
+    {
+        get => _defaultMinimumHeight;
+        set => _defaultMinimumHeight = value;
+    }
+
     /// <summary>
-    ///     The index of the selected button, or -1 if the user pressed <see cref="Application.QuitKey"/> to close the MessageBox. This is useful for web
-    ///     based console where there is no SynchronizationContext or TaskScheduler.
+    ///     The index of the selected button, or <see langword="null"/> if the user pressed <see cref="Application.QuitKey"/>.
     /// </summary>
     /// <remarks>
     ///     <para>
@@ -65,55 +115,97 @@ public static class MessageBox
     ///         non-global alternative for custom dialog implementations.
     ///     </para>
     /// </remarks>
-    public static int Clicked { get; private set; } = -1;
+    public static int? Clicked { get; private set; }
 
     /// <summary>
-    ///     Presents an error <see cref="MessageBox"/> with the specified title and message and a list of buttons.
+    ///     Displays an error <see cref="MessageBox"/> with fixed dimensions.
     /// </summary>
-    /// <returns>The index of the selected button, or -1 if the user pressed <see cref="Application.QuitKey"/> to close the MessageBox.</returns>
+    /// <param name="app">The application instance. If <see langword="null"/>, uses <see cref="IApplication.TopRunnable"/>.</param>
     /// <param name="width">Width for the MessageBox.</param>
     /// <param name="height">Height for the MessageBox.</param>
     /// <param name="title">Title for the MessageBox.</param>
-    /// <param name="message">Message to display; might contain multiple lines. The message will be word=wrapped by default.</param>
-    /// <param name="buttons">Array of buttons to add.</param>
+    /// <param name="message">Message to display. May contain multiple lines and will be word-wrapped.</param>
+    /// <param name="buttons">Array of button labels.</param>
+    /// <returns>
+    ///     The index of the selected button, or <see langword="null"/> if the user pressed
+    ///     <see cref="Application.QuitKey"/>.
+    /// </returns>
+    /// <exception cref="ArgumentNullException">Thrown if <paramref name="app"/> is <see langword="null"/>.</exception>
     /// <remarks>
-    ///     Use <see cref="ErrorQuery(string, string, string[])"/> instead; it automatically sizes the MessageBox based on
-    ///     the contents.
+    ///     Consider using <see cref="ErrorQuery(IApplication?, string, string, string[])"/> which automatically sizes the
+    ///     MessageBox.
     /// </remarks>
-    public static int ErrorQuery (int width, int height, string title, string message, params string [] buttons)
+    public static int? ErrorQuery (
+        IApplication? app,
+        int width,
+        int height,
+        string title,
+        string message,
+        params string [] buttons
+    )
     {
-        return QueryFull (true, width, height, title, message, 0, true, buttons);
+        return QueryFull (
+                          app,
+                          true,
+                          width,
+                          height,
+                          title,
+                          message,
+                          0,
+                          true,
+                          buttons);
     }
 
     /// <summary>
-    ///     Presents an error <see cref="MessageBox"/> with the specified title and message and a list of buttons to show
-    ///     to the user.
+    ///     Displays an auto-sized error <see cref="MessageBox"/>.
     /// </summary>
-    /// <returns>The index of the selected button, or -1 if the user pressed <see cref="Application.QuitKey"/> to close the MessageBox.</returns>
-    /// <param name="title">Title for the query.</param>
-    /// <param name="message">Message to display; might contain multiple lines. The message will be word=wrapped by default.</param>
-    /// <param name="buttons">Array of buttons to add.</param>
+    /// <param name="app">The application instance. If <see langword="null"/>, uses <see cref="IApplication.TopRunnable"/>.</param>
+    /// <param name="title">Title for the MessageBox.</param>
+    /// <param name="message">Message to display. May contain multiple lines and will be word-wrapped.</param>
+    /// <param name="buttons">Array of button labels.</param>
+    /// <returns>
+    ///     The index of the selected button, or <see langword="null"/> if the user pressed
+    ///     <see cref="Application.QuitKey"/>.
+    /// </returns>
+    /// <exception cref="ArgumentNullException">Thrown if <paramref name="app"/> is <see langword="null"/>.</exception>
     /// <remarks>
-    ///     The message box will be vertically and horizontally centered in the container and the size will be
-    ///     automatically determined from the size of the title, message. and buttons.
+    ///     The MessageBox is centered and auto-sized based on title, message, and buttons.
     /// </remarks>
-    public static int ErrorQuery (string title, string message, params string [] buttons) { return QueryFull (true, 0, 0, title, message, 0, true, buttons); }
+    public static int? ErrorQuery (IApplication? app, string title, string message, params string [] buttons)
+    {
+        return QueryFull (
+                          app,
+                          true,
+                          0,
+                          0,
+                          title,
+                          message,
+                          0,
+                          true,
+                          buttons);
+    }
 
     /// <summary>
-    ///     Presents an error <see cref="MessageBox"/> with the specified title and message and a list of buttons.
+    ///     Displays an error <see cref="MessageBox"/> with fixed dimensions and a default button.
     /// </summary>
-    /// <returns>The index of the selected button, or -1 if the user pressed <see cref="Application.QuitKey"/> to close the MessageBox.</returns>
+    /// <param name="app">The application instance. If <see langword="null"/>, uses <see cref="IApplication.TopRunnable"/>.</param>
     /// <param name="width">Width for the MessageBox.</param>
     /// <param name="height">Height for the MessageBox.</param>
     /// <param name="title">Title for the MessageBox.</param>
-    /// <param name="message">Message to display; might contain multiple lines. The message will be word=wrapped by default.</param>
-    /// <param name="defaultButton">Index of the default button.</param>
-    /// <param name="buttons">Array of buttons to add.</param>
+    /// <param name="message">Message to display. May contain multiple lines and will be word-wrapped.</param>
+    /// <param name="defaultButton">Index of the default button (0-based).</param>
+    /// <param name="buttons">Array of button labels.</param>
+    /// <returns>
+    ///     The index of the selected button, or <see langword="null"/> if the user pressed
+    ///     <see cref="Application.QuitKey"/>.
+    /// </returns>
+    /// <exception cref="ArgumentNullException">Thrown if <paramref name="app"/> is <see langword="null"/>.</exception>
     /// <remarks>
-    ///     Use <see cref="ErrorQuery(string, string, string[])"/> instead; it automatically sizes the MessageBox based on
-    ///     the contents.
+    ///     Consider using <see cref="ErrorQuery(IApplication?, string, string, int, string[])"/> which automatically sizes the
+    ///     MessageBox.
     /// </remarks>
-    public static int ErrorQuery (
+    public static int? ErrorQuery (
+        IApplication? app,
         int width,
         int height,
         string title,
@@ -122,44 +214,73 @@ public static class MessageBox
         params string [] buttons
     )
     {
-        return QueryFull (true, width, height, title, message, defaultButton, true, buttons);
+        return QueryFull (
+                          app,
+                          true,
+                          width,
+                          height,
+                          title,
+                          message,
+                          defaultButton,
+                          true,
+                          buttons);
     }
 
     /// <summary>
-    ///     Presents an error <see cref="MessageBox"/> with the specified title and message and a list of buttons to show
-    ///     to the user.
+    ///     Displays an auto-sized error <see cref="MessageBox"/> with a default button.
     /// </summary>
-    /// <returns>The index of the selected button, or -1 if the user pressed <see cref="Application.QuitKey"/> to close the MessageBox.</returns>
+    /// <param name="app">The application instance. If <see langword="null"/>, uses <see cref="IApplication.TopRunnable"/>.</param>
     /// <param name="title">Title for the MessageBox.</param>
-    /// <param name="message">Message to display; might contain multiple lines. The message will be word=wrapped by default.</param>
-    /// <param name="defaultButton">Index of the default button.</param>
-    /// <param name="buttons">Array of buttons to add.</param>
+    /// <param name="message">Message to display. May contain multiple lines and will be word-wrapped.</param>
+    /// <param name="defaultButton">Index of the default button (0-based).</param>
+    /// <param name="buttons">Array of button labels.</param>
+    /// <returns>
+    ///     The index of the selected button, or <see langword="null"/> if the user pressed
+    ///     <see cref="Application.QuitKey"/>.
+    /// </returns>
+    /// <exception cref="ArgumentNullException">Thrown if <paramref name="app"/> is <see langword="null"/>.</exception>
     /// <remarks>
-    ///     The message box will be vertically and horizontally centered in the container and the size will be
-    ///     automatically determined from the size of the title, message. and buttons.
+    ///     The MessageBox is centered and auto-sized based on title, message, and buttons.
     /// </remarks>
-    public static int ErrorQuery (string title, string message, int defaultButton = 0, params string [] buttons)
+    public static int? ErrorQuery (IApplication? app, string title, string message, int defaultButton = 0, params string [] buttons)
     {
-        return QueryFull (true, 0, 0, title, message, defaultButton, true, buttons);
+        return QueryFull (
+                          app,
+                          true,
+                          0,
+                          0,
+                          title,
+                          message,
+                          defaultButton,
+                          true,
+                          buttons);
     }
 
     /// <summary>
-    ///     Presents an error <see cref="MessageBox"/> with the specified title and message and a list of buttons to show
-    ///     to the user.
+    ///     Displays an error <see cref="MessageBox"/> with fixed dimensions, a default button, and word-wrap control.
     /// </summary>
-    /// <returns>The index of the selected button, or -1 if the user pressed <see cref="Application.QuitKey"/> to close the MessageBox.</returns>
-    /// <param name="width">Width for the window.</param>
-    /// <param name="height">Height for the window.</param>
-    /// <param name="title">Title for the query.</param>
-    /// <param name="message">Message to display; might contain multiple lines. The message will be word=wrapped by default.</param>
-    /// <param name="defaultButton">Index of the default button.</param>
-    /// <param name="wrapMessage">If wrap the message or not.</param>
-    /// <param name="buttons">Array of buttons to add.</param>
+    /// <param name="app">The application instance. If <see langword="null"/>, uses <see cref="IApplication.TopRunnable"/>.</param>
+    /// <param name="width">Width for the MessageBox.</param>
+    /// <param name="height">Height for the MessageBox.</param>
+    /// <param name="title">Title for the MessageBox.</param>
+    /// <param name="message">Message to display. May contain multiple lines.</param>
+    /// <param name="defaultButton">Index of the default button (0-based).</param>
+    /// <param name="wrapMessage">
+    ///     If <see langword="true"/>, word-wraps the message; otherwise displays as-is with multi-line
+    ///     support.
+    /// </param>
+    /// <param name="buttons">Array of button labels.</param>
+    /// <returns>
+    ///     The index of the selected button, or <see langword="null"/> if the user pressed
+    ///     <see cref="Application.QuitKey"/>.
+    /// </returns>
+    /// <exception cref="ArgumentNullException">Thrown if <paramref name="app"/> is <see langword="null"/>.</exception>
     /// <remarks>
-    ///     Use <see cref="ErrorQuery(string, string, string[])"/> instead; it automatically sizes the MessageBox based on
-    ///     the contents.
+    ///     Consider using <see cref="ErrorQuery(IApplication?, string, string, int, bool, string[])"/> which automatically
+    ///     sizes the MessageBox.
     /// </remarks>
-    public static int ErrorQuery (
+    public static int? ErrorQuery (
+        IApplication? app,
         int width,
         int height,
         string title,
@@ -169,24 +290,40 @@ public static class MessageBox
         params string [] buttons
     )
     {
-        return QueryFull (true, width, height, title, message, defaultButton, wrapMessage, buttons);
+        return QueryFull (
+                          app,
+                          true,
+                          width,
+                          height,
+                          title,
+                          message,
+                          defaultButton,
+                          wrapMessage,
+                          buttons);
     }
 
     /// <summary>
-    ///     Presents an error <see cref="MessageBox"/> with the specified title and message and a list of buttons to show
-    ///     to the user.
+    ///     Displays an auto-sized error <see cref="MessageBox"/> with a default button and word-wrap control.
     /// </summary>
-    /// <returns>The index of the selected button, or -1 if the user pressed <see cref="Application.QuitKey"/> to close the MessageBox.</returns>
-    /// <param name="title">Title for the query.</param>
-    /// <param name="message">Message to display; might contain multiple lines. The message will be word=wrapped by default.</param>
-    /// <param name="defaultButton">Index of the default button.</param>
-    /// <param name="wrapMessage">If wrap the message or not. The default is <see langword="true"/></param>
-    /// <param name="buttons">Array of buttons to add.</param>
+    /// <param name="app">The application instance. If <see langword="null"/>, uses <see cref="IApplication.TopRunnable"/>.</param>
+    /// <param name="title">Title for the MessageBox.</param>
+    /// <param name="message">Message to display. May contain multiple lines.</param>
+    /// <param name="defaultButton">Index of the default button (0-based).</param>
+    /// <param name="wrapMessage">
+    ///     If <see langword="true"/>, word-wraps the message; otherwise displays as-is with multi-line
+    ///     support.
+    /// </param>
+    /// <param name="buttons">Array of button labels.</param>
+    /// <returns>
+    ///     The index of the selected button, or <see langword="null"/> if the user pressed
+    ///     <see cref="Application.QuitKey"/>.
+    /// </returns>
+    /// <exception cref="ArgumentNullException">Thrown if <paramref name="app"/> is <see langword="null"/>.</exception>
     /// <remarks>
-    ///     The message box will be vertically and horizontally centered in the container and the size will be
-    ///     automatically determined from the size of the title, message. and buttons.
+    ///     The MessageBox is centered and auto-sized based on title, message, and buttons.
     /// </remarks>
-    public static int ErrorQuery (
+    public static int? ErrorQuery (
+        IApplication? app,
         string title,
         string message,
         int defaultButton = 0,
@@ -194,67 +331,100 @@ public static class MessageBox
         params string [] buttons
     )
     {
-        return QueryFull (true, 0, 0, title, message, defaultButton, wrapMessage, buttons);
+        return QueryFull (
+                          app,
+                          true,
+                          0,
+                          0,
+                          title,
+                          message,
+                          defaultButton,
+                          wrapMessage,
+                          buttons);
     }
 
     /// <summary>
-    ///     Presents a <see cref="MessageBox"/> with the specified title and message and a list of buttons.
+    ///     Displays a <see cref="MessageBox"/> with fixed dimensions.
     /// </summary>
-    /// <returns>The index of the selected button, or -1 if the user pressed <see cref="Application.QuitKey"/> to close the MessageBox.</returns>
+    /// <param name="app">The application instance. If <see langword="null"/>, uses <see cref="IApplication.TopRunnable"/>.</param>
     /// <param name="width">Width for the MessageBox.</param>
     /// <param name="height">Height for the MessageBox.</param>
     /// <param name="title">Title for the MessageBox.</param>
-    /// <param name="message">Message to display; might contain multiple lines. The message will be word=wrapped by default.</param>
-    /// <param name="buttons">Array of buttons to add.</param>
+    /// <param name="message">Message to display. May contain multiple lines and will be word-wrapped.</param>
+    /// <param name="buttons">Array of button labels.</param>
+    /// <returns>
+    ///     The index of the selected button, or <see langword="null"/> if the user pressed
+    ///     <see cref="Application.QuitKey"/>.
+    /// </returns>
+    /// <exception cref="ArgumentNullException">Thrown if <paramref name="app"/> is <see langword="null"/>.</exception>
     /// <remarks>
-    ///     Use <see cref="Query(string, string, string[])"/> instead; it automatically sizes the MessageBox based on
-    ///     the contents.
+    ///     Consider using <see cref="Query(IApplication?, string, string, string[])"/> which automatically sizes the
+    ///     MessageBox.
     /// </remarks>
-    public static int Query (int width, int height, string title, string message, params string [] buttons)
+    public static int? Query (IApplication? app, int width, int height, string title, string message, params string [] buttons)
     {
-        return QueryFull (false, width, height, title, message, 0, true, buttons);
+        return QueryFull (
+                          app,
+                          false,
+                          width,
+                          height,
+                          title,
+                          message,
+                          0,
+                          true,
+                          buttons);
     }
 
     /// <summary>
-    ///     Presents a <see cref="MessageBox"/> with the specified title and message and a list of buttons.
+    ///     Displays an auto-sized <see cref="MessageBox"/>.
     /// </summary>
-    /// <returns>The index of the selected button, or -1 if the user pressed <see cref="Application.QuitKey"/> to close the MessageBox.</returns>
+    /// <param name="app">The application instance. If <see langword="null"/>, uses <see cref="IApplication.TopRunnable"/>.</param>
     /// <param name="title">Title for the MessageBox.</param>
-    /// <param name="message">Message to display; might contain multiple lines. The message will be word=wrapped by default.</param>
-    /// <param name="buttons">Array of buttons to add.</param>
+    /// <param name="message">Message to display. May contain multiple lines and will be word-wrapped.</param>
+    /// <param name="buttons">Array of button labels.</param>
+    /// <returns>
+    ///     The index of the selected button, or <see langword="null"/> if the user pressed
+    ///     <see cref="Application.QuitKey"/>.
+    /// </returns>
+    /// <exception cref="ArgumentNullException">Thrown if <paramref name="app"/> is <see langword="null"/>.</exception>
     /// <remarks>
-    /// <para>
-    ///     The message box will be vertically and horizontally centered in the container and the size will be
-    ///     automatically determined from the size of the title, message. and buttons.
-    /// </para>
-    /// <para>
-    ///     Use <see cref="Query(string, string, string[])"/> instead; it automatically sizes the MessageBox based on
-    ///     the contents.
-    /// </para>
+    ///     The MessageBox is centered and auto-sized based on title, message, and buttons.
     /// </remarks>
-    public static int Query (string title, string message, params string [] buttons) { return QueryFull (false, 0, 0, title, message, 0, true, buttons); }
+    public static int? Query (IApplication? app, string title, string message, params string [] buttons)
+    {
+        return QueryFull (
+                          app,
+                          false,
+                          0,
+                          0,
+                          title,
+                          message,
+                          0,
+                          true,
+                          buttons);
+    }
 
     /// <summary>
-    ///     Presents a <see cref="MessageBox"/> with the specified title and message and a list of buttons.
+    ///     Displays a <see cref="MessageBox"/> with fixed dimensions and a default button.
     /// </summary>
-    /// <returns>The index of the selected button, or -1 if the user pressed <see cref="Application.QuitKey"/> to close the MessageBox.</returns>
-    /// <param name="width">Width for the window.</param>
-    /// <param name="height">Height for the window.</param>
+    /// <param name="app">The application instance. If <see langword="null"/>, uses <see cref="IApplication.TopRunnable"/>.</param>
+    /// <param name="width">Width for the MessageBox.</param>
+    /// <param name="height">Height for the MessageBox.</param>
     /// <param name="title">Title for the MessageBox.</param>
-    /// <param name="message">Message to display; might contain multiple lines. The message will be word=wrapped by default.</param>
-    /// <param name="defaultButton">Index of the default button.</param>
-    /// <param name="buttons">Array of buttons to add.</param>
+    /// <param name="message">Message to display. May contain multiple lines and will be word-wrapped.</param>
+    /// <param name="defaultButton">Index of the default button (0-based).</param>
+    /// <param name="buttons">Array of button labels.</param>
+    /// <returns>
+    ///     The index of the selected button, or <see langword="null"/> if the user pressed
+    ///     <see cref="Application.QuitKey"/>.
+    /// </returns>
+    /// <exception cref="ArgumentNullException">Thrown if <paramref name="app"/> is <see langword="null"/>.</exception>
     /// <remarks>
-    /// <para>
-    ///     The message box will be vertically and horizontally centered in the container and the size will be
-    ///     automatically determined from the size of the title, message. and buttons.
-    /// </para>
-    /// <para>
-    ///     Use <see cref="Query(string, string, string[])"/> instead; it automatically sizes the MessageBox based on
-    ///     the contents.
-    /// </para>
+    ///     Consider using <see cref="Query(IApplication?, string, string, int, string[])"/> which automatically sizes the
+    ///     MessageBox.
     /// </remarks>
-    public static int Query (
+    public static int? Query (
+        IApplication? app,
         int width,
         int height,
         string title,
@@ -263,43 +433,73 @@ public static class MessageBox
         params string [] buttons
     )
     {
-        return QueryFull (false, width, height, title, message, defaultButton, true, buttons);
+        return QueryFull (
+                          app,
+                          false,
+                          width,
+                          height,
+                          title,
+                          message,
+                          defaultButton,
+                          true,
+                          buttons);
     }
 
     /// <summary>
-    ///     Presents a <see cref="MessageBox"/> with the specified title and message and a list of buttons.
+    ///     Displays an auto-sized <see cref="MessageBox"/> with a default button.
     /// </summary>
-    /// <returns>The index of the selected button, or -1 if the user pressed <see cref="Application.QuitKey"/> to close the MessageBox.</returns>
+    /// <param name="app">The application instance. If <see langword="null"/>, uses <see cref="IApplication.TopRunnable"/>.</param>
     /// <param name="title">Title for the MessageBox.</param>
-    /// <param name="message">Message to display; might contain multiple lines. The message will be word=wrapped by default.</param>
-    /// <param name="defaultButton">Index of the default button.</param>
-    /// <param name="buttons">Array of buttons to add.</param>
+    /// <param name="message">Message to display. May contain multiple lines and will be word-wrapped.</param>
+    /// <param name="defaultButton">Index of the default button (0-based).</param>
+    /// <param name="buttons">Array of button labels.</param>
+    /// <returns>
+    ///     The index of the selected button, or <see langword="null"/> if the user pressed
+    ///     <see cref="Application.QuitKey"/>.
+    /// </returns>
+    /// <exception cref="ArgumentNullException">Thrown if <paramref name="app"/> is <see langword="null"/>.</exception>
     /// <remarks>
-    ///     The message box will be vertically and horizontally centered in the container and the size will be
-    ///     automatically determined from the size of the message and buttons.
+    ///     The MessageBox is centered and auto-sized based on title, message, and buttons.
     /// </remarks>
-    public static int Query (string title, string message, int defaultButton = 0, params string [] buttons)
+    public static int? Query (IApplication? app, string title, string message, int defaultButton = 0, params string [] buttons)
     {
-        return QueryFull (false, 0, 0, title, message, defaultButton, true, buttons);
+        return QueryFull (
+                          app,
+                          false,
+                          0,
+                          0,
+                          title,
+                          message,
+                          defaultButton,
+                          true,
+                          buttons);
     }
 
     /// <summary>
-    ///     Presents a <see cref="MessageBox"/> with the specified title and message and a list of buttons to show
-    ///     to the user.
+    ///     Displays a <see cref="MessageBox"/> with fixed dimensions, a default button, and word-wrap control.
     /// </summary>
-    /// <returns>The index of the selected button, or -1 if the user pressed <see cref="Application.QuitKey"/> to close the MessageBox.</returns>
-    /// <param name="width">Width for the window.</param>
-    /// <param name="height">Height for the window.</param>
-    /// <param name="title">Title for the query.</param>
-    /// <param name="message">Message to display, might contain multiple lines.</param>
-    /// <param name="defaultButton">Index of the default button.</param>
-    /// <param name="wrapMessage">If wrap the message or not.</param>
-    /// <param name="buttons">Array of buttons to add.</param>
+    /// <param name="app">The application instance. If <see langword="null"/>, uses <see cref="IApplication.TopRunnable"/>.</param>
+    /// <param name="width">Width for the MessageBox.</param>
+    /// <param name="height">Height for the MessageBox.</param>
+    /// <param name="title">Title for the MessageBox.</param>
+    /// <param name="message">Message to display. May contain multiple lines.</param>
+    /// <param name="defaultButton">Index of the default button (0-based).</param>
+    /// <param name="wrapMessage">
+    ///     If <see langword="true"/>, word-wraps the message; otherwise displays as-is with multi-line
+    ///     support.
+    /// </param>
+    /// <param name="buttons">Array of button labels.</param>
+    /// <returns>
+    ///     The index of the selected button, or <see langword="null"/> if the user pressed
+    ///     <see cref="Application.QuitKey"/>.
+    /// </returns>
+    /// <exception cref="ArgumentNullException">Thrown if <paramref name="app"/> is <see langword="null"/>.</exception>
     /// <remarks>
-    ///     Use <see cref="Query(string, string, string[])"/> instead; it automatically sizes the MessageBox based on the
-    ///     contents.
+    ///     Consider using <see cref="Query(IApplication?, string, string, int, bool, string[])"/> which automatically sizes
+    ///     the MessageBox.
     /// </remarks>
-    public static int Query (
+    public static int? Query (
+        IApplication? app,
         int width,
         int height,
         string title,
@@ -309,20 +509,40 @@ public static class MessageBox
         params string [] buttons
     )
     {
-        return QueryFull (false, width, height, title, message, defaultButton, wrapMessage, buttons);
+        return QueryFull (
+                          app,
+                          false,
+                          width,
+                          height,
+                          title,
+                          message,
+                          defaultButton,
+                          wrapMessage,
+                          buttons);
     }
 
     /// <summary>
-    ///     Presents a <see cref="MessageBox"/> with the specified title and message and a list of buttons to show
-    ///     to the user.
+    ///     Displays an auto-sized <see cref="MessageBox"/> with a default button and word-wrap control.
     /// </summary>
-    /// <returns>The index of the selected button, or -1 if the user pressed <see cref="Application.QuitKey"/> to close the MessageBox.</returns>
-    /// <param name="title">Title for the query.</param>
-    /// <param name="message">Message to display, might contain multiple lines.</param>
-    /// <param name="defaultButton">Index of the default button.</param>
-    /// <param name="wrapMessage">If wrap the message or not.</param>
-    /// <param name="buttons">Array of buttons to add.</param>
-    public static int Query (
+    /// <param name="app">The application instance. If <see langword="null"/>, uses <see cref="IApplication.TopRunnable"/>.</param>
+    /// <param name="title">Title for the MessageBox.</param>
+    /// <param name="message">Message to display. May contain multiple lines.</param>
+    /// <param name="defaultButton">Index of the default button (0-based).</param>
+    /// <param name="wrapMessage">
+    ///     If <see langword="true"/>, word-wraps the message; otherwise displays as-is with multi-line
+    ///     support.
+    /// </param>
+    /// <param name="buttons">Array of button labels.</param>
+    /// <returns>
+    ///     The index of the selected button, or <see langword="null"/> if the user pressed
+    ///     <see cref="Application.QuitKey"/>.
+    /// </returns>
+    /// <exception cref="ArgumentNullException">Thrown if <paramref name="app"/> is <see langword="null"/>.</exception>
+    /// <remarks>
+    ///     The MessageBox is centered and auto-sized based on title, message, and buttons.
+    /// </remarks>
+    public static int? Query (
+        IApplication? app,
         string title,
         string message,
         int defaultButton = 0,
@@ -330,10 +550,20 @@ public static class MessageBox
         params string [] buttons
     )
     {
-        return QueryFull (false, 0, 0, title, message, defaultButton, wrapMessage, buttons);
+        return QueryFull (
+                          app,
+                          false,
+                          0,
+                          0,
+                          title,
+                          message,
+                          defaultButton,
+                          wrapMessage,
+                          buttons);
     }
 
-    private static int QueryFull (
+    private static int? QueryFull (
+        IApplication? app,
         bool useErrorColors,
         int width,
         int height,
@@ -344,6 +574,8 @@ public static class MessageBox
         params string [] buttons
     )
     {
+        ArgumentNullException.ThrowIfNull (app);
+
         // Create button array for Dialog
         var count = 0;
         List<Button> buttonList = new ();
@@ -384,8 +616,9 @@ public static class MessageBox
                                        e.Handled = true;
                                    }
 
-                                   Application.RequestStop ();
-                               };
+                                       (s as View)?.App?.RequestStop ();
+                                   };
+                }
 
                 buttonList.Add (b);
                 count++;
@@ -395,20 +628,21 @@ public static class MessageBox
         var d = new Dialog
         {
             Title = title,
-            ButtonAlignment = MessageBox.DefaultButtonAlignment,
+            ButtonAlignment = DefaultButtonAlignment,
             ButtonAlignmentModes = AlignmentModes.StartToEnd | AlignmentModes.AddSpaceBetweenItems,
-            BorderStyle = MessageBox.DefaultBorderStyle,
-            Buttons = buttonList.ToArray (),
+            BorderStyle = DefaultBorderStyle,
+            Buttons = buttonList.ToArray ()
         };
 
-        d.Width = Dim.Auto (DimAutoStyle.Auto,
-                            minimumContentDim: Dim.Func (_ => (int)((Application.Screen.Width - d.GetAdornmentsThickness ().Horizontal) * (DefaultMinimumWidth / 100f))),
-                            maximumContentDim: Dim.Func (_ => (int)((Application.Screen.Width - d.GetAdornmentsThickness ().Horizontal) * 0.9f)));
-
-        d.Height = Dim.Auto (DimAutoStyle.Auto,
-                             minimumContentDim: Dim.Func (_ => (int)((Application.Screen.Height - d.GetAdornmentsThickness ().Vertical) * (DefaultMinimumHeight / 100f))),
-                             maximumContentDim: Dim.Func (_ => (int)((Application.Screen.Height - d.GetAdornmentsThickness ().Vertical) * 0.9f)));
+        d.Width = Dim.Auto (
+                            DimAutoStyle.Auto,
+                            Dim.Func (_ => (int)((app.Screen.Width - d.GetAdornmentsThickness ().Horizontal) * (DefaultMinimumWidth / 100f))),
+                            Dim.Func (_ => (int)((app.Screen.Width - d.GetAdornmentsThickness ().Horizontal) * 0.9f)));
 
+        d.Height = Dim.Auto (
+                             DimAutoStyle.Auto,
+                             Dim.Func (_ => (int)((app.Screen.Height - d.GetAdornmentsThickness ().Vertical) * (DefaultMinimumHeight / 100f))),
+                             Dim.Func (_ => (int)((app.Screen.Height - d.GetAdornmentsThickness ().Vertical) * 0.9f)));
 
         if (width != 0)
         {
@@ -422,7 +656,7 @@ public static class MessageBox
 
         d.SchemeName = useErrorColors ? SchemeManager.SchemesToSchemeName (Schemes.Error) : SchemeManager.SchemesToSchemeName (Schemes.Dialog);
 
-        d.HotKeySpecifier = new Rune ('\xFFFF');
+        d.HotKeySpecifier = new ('\xFFFF');
         d.Text = message;
         d.TextAlignment = Alignment.Center;
         d.VerticalTextAlignment = Alignment.Start;
@@ -430,8 +664,8 @@ public static class MessageBox
         d.TextFormatter.MultiLine = !wrapMessage;
 
         // Run the modal; do not shut down the mainloop driver when done
-        Application.Run (d);
-        
+        app.Run (d);
+
         // Use Dialog.Result instead of manually tracking with Clicked
         // Dialog automatically extracts which button was clicked in OnIsRunningChanging
         int result = d.Result ?? -1;

+ 1 - 1
Terminal.Gui/Views/Selectors/SelectorBase.cs

@@ -425,7 +425,7 @@ public abstract class SelectorBase : View, IOrientation
             maxNaturalCheckBoxWidth = SubViews.OfType<CheckBox> ().Max (
                                                              v =>
                                                              {
-                                                                 v.SetRelativeLayout (Application.Screen.Size);
+                                                                 v.SetRelativeLayout (App?.Screen.Size ?? new Size (2048, 2048));
                                                                  v.Layout ();
                                                                  return v.Frame.Width;
                                                              });

+ 8 - 2
Terminal.Gui/Views/StatusBar.cs

@@ -9,6 +9,8 @@ namespace Terminal.Gui.Views;
 /// </summary>
 public class StatusBar : Bar, IDesignable
 {
+    private static LineStyle _defaultSeparatorLineStyle = LineStyle.Single; // Resources/config.json overrides
+
     /// <inheritdoc/>
     public StatusBar () : this ([]) { }
 
@@ -55,7 +57,11 @@ public class StatusBar : Bar, IDesignable
     ///     Gets or sets the default Line Style for the separators between the shortcuts of the StatusBar.
     /// </summary>
     [ConfigurationProperty (Scope = typeof (ThemeScope))]
-    public static LineStyle DefaultSeparatorLineStyle { get; set; } = LineStyle.Single;
+    public static LineStyle DefaultSeparatorLineStyle
+    {
+        get => _defaultSeparatorLineStyle;
+        set => _defaultSeparatorLineStyle = value;
+    }
 
     /// <inheritdoc />
     protected override void OnSubViewLayout (LayoutEventArgs args)
@@ -160,7 +166,7 @@ public class StatusBar : Bar, IDesignable
 
         return true;
 
-        void OnButtonClicked (object? sender, EventArgs? e) { MessageBox.Query ("Hi", $"You clicked {sender}"); }
+        void OnButtonClicked (object? sender, EventArgs? e) { MessageBox.Query (App, "Hi", $"You clicked {sender}"); }
     }
 
     /// <inheritdoc />

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

@@ -1534,7 +1534,7 @@ public class TableView : View, IDesignable
     /// <param name="width"></param>
     private void ClearLine (int row, int width)
     {
-        if (Application.Screen.Height == 0)
+        if (App?.Screen.Height == 0)
         {
             return;
         }
@@ -1810,7 +1810,7 @@ public class TableView : View, IDesignable
                 }
             }
 
-            if (Application.Screen.Height > 0)
+            if (App?.Screen.Height > 0)
             {
                 AddRuneAt (c, row, rune);
             }

+ 5 - 5
Terminal.Gui/Views/TextInput/TextField.cs

@@ -617,7 +617,7 @@ public class TextField : View, IDesignable
             return;
         }
 
-        Clipboard.Contents = SelectedText;
+        App?.Clipboard?.SetClipboardData (SelectedText);
     }
 
     /// <summary>Cut the selected text to the clipboard.</summary>
@@ -628,7 +628,7 @@ public class TextField : View, IDesignable
             return;
         }
 
-        Clipboard.Contents = SelectedText;
+        App?.Clipboard?.SetClipboardData (SelectedText);
         List<string> newText = DeleteSelectedText ();
         Text = StringExtensions.ToString (newText);
         Adjust ();
@@ -1079,7 +1079,7 @@ public class TextField : View, IDesignable
             return;
         }
 
-        string cbTxt = Clipboard.Contents.Split ("\n") [0] ?? "";
+        string cbTxt = App?.Clipboard?.GetClipboardData ()?.Split ("\n") [0];
 
         if (string.IsNullOrEmpty (cbTxt))
         {
@@ -1731,9 +1731,9 @@ public class TextField : View, IDesignable
 
     private void SetClipboard (IEnumerable<string> text)
     {
-        if (!Secret)
+        if (!Secret && App?.Clipboard is { })
         {
-            Clipboard.Contents = StringExtensions.ToString (text.ToList ());
+            App.Clipboard.SetClipboardData (StringExtensions.ToString (text.ToList ()));
         }
     }
 

+ 4 - 4
Terminal.Gui/Views/TextInput/TextView.cs

@@ -1960,7 +1960,7 @@ public class TextView : View, IDesignable
         }
 
         SetWrapModel ();
-        string? contents = Clipboard.Contents;
+        string? contents = App?.Clipboard?.GetClipboardData ();
 
         if (_copyWithoutSelection && contents!.FirstOrDefault (x => x is '\n' or '\r') == 0)
         {
@@ -2363,7 +2363,7 @@ public class TextView : View, IDesignable
         OnUnwrappedCursorPosition ();
     }
 
-    private void AppendClipboard (string text) { Clipboard.Contents += text; }
+    private void AppendClipboard (string text) { App?.Clipboard?.SetClipboardData (App?.Clipboard?.GetClipboardData () + text); }
 
     private PopoverMenu CreateContextMenu ()
     {
@@ -3842,7 +3842,7 @@ public class TextView : View, IDesignable
 
             List<Cell> currentLine = GetCurrentLine ();
 
-            if (currentLine.Count > 0 && currentLine[CurrentColumn - 1].Grapheme == "\t")
+            if (currentLine.Count > 0 && currentLine [CurrentColumn - 1].Grapheme == "\t")
             {
                 _historyText.Add (new () { new (currentLine) }, CursorPosition);
 
@@ -4470,7 +4470,7 @@ public class TextView : View, IDesignable
     {
         if (text is { })
         {
-            Clipboard.Contents = text;
+            App?.Clipboard?.SetClipboardData (text);
         }
     }
 

+ 13 - 4
Terminal.Gui/Views/Window.cs

@@ -1,5 +1,3 @@
-
-
 namespace Terminal.Gui.Views;
 
 /// <summary>
@@ -18,6 +16,9 @@ namespace Terminal.Gui.Views;
 /// <seealso cref="FrameView"/>
 public class Window : Toplevel
 {
+    private static ShadowStyle _defaultShadow = ShadowStyle.None; // Resources/config.json overrides
+    private static LineStyle _defaultBorderStyle = LineStyle.Single; // Resources/config.json overrides
+
     /// <summary>
     ///     Initializes a new instance of the <see cref="Window"/> class.
     /// </summary>
@@ -35,7 +36,11 @@ public class Window : Toplevel
     ///     Gets or sets whether all <see cref="Window"/>s are shown with a shadow effect by default.
     /// </summary>
     [ConfigurationProperty (Scope = typeof (ThemeScope))]
-    public static ShadowStyle DefaultShadow { get; set; } = ShadowStyle.None;
+    public static ShadowStyle DefaultShadow
+    {
+        get => _defaultShadow;
+        set => _defaultShadow = value;
+    }
 
     // TODO: enable this
     ///// <summary>
@@ -56,5 +61,9 @@ public class Window : Toplevel
     ///     s.
     /// </remarks>
     [ConfigurationProperty (Scope = typeof (ThemeScope))]
-    public static LineStyle DefaultBorderStyle { get; set; } = LineStyle.Single;
+    public static LineStyle DefaultBorderStyle
+    {
+        get => _defaultBorderStyle;
+        set => _defaultBorderStyle = value;
+    }
 }

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

@@ -487,7 +487,7 @@ public class Wizard : Dialog
 
                 if (IsCurrentTop)
                 {
-                    Application.RequestStop (this);
+                    (sender as View)?.App?.RequestStop (this);
                     e.Handled = true;
                 }
 

+ 12 - 11
Tests/IntegrationTests/FluentTests/FileDialogFluentTests.cs

@@ -60,10 +60,10 @@ public class FileDialogFluentTests
     public void CancelFileDialog_QuitKey_Quits (TestDriver d)
     {
         SaveDialog? sd = null;
-        using var c = With.A (() => NewSaveDialog (out sd), 100, 20, d)
-            .ScreenShot ("Save dialog", _out)
-            .EnqueueKeyEvent (Application.QuitKey)
-            .AssertTrue (sd!.Canceled);
+        using GuiTestContext c = With.A (() => NewSaveDialog (out sd), 100, 20, d, logWriter: _out)
+                                     .ScreenShot ("Save dialog", _out)
+                                     .EnqueueKeyEvent (Application.QuitKey)
+                                     .AssertTrue (sd!.Canceled);
     }
 
     [Theory]
@@ -93,7 +93,7 @@ public class FileDialogFluentTests
     public void CancelFileDialog_UsingCancelButton_AltC (TestDriver d)
     {
         SaveDialog? sd = null;
-        using var c = With.A (() => NewSaveDialog (out sd), 100, 20, d)
+        using var c = With.A (() => NewSaveDialog (out sd), 100, 20, d, _out)
                           .ScreenShot ("Save dialog", _out)
                           .EnqueueKeyEvent (Key.C.WithAlt)
                           .AssertTrue (sd!.Canceled);
@@ -132,12 +132,13 @@ public class FileDialogFluentTests
     {
         SaveDialog? sd = null;
         MockFileSystem? fs = null;
-        using var c = With.A (() => NewSaveDialog (out sd, out fs, modal: false), 100, 20, d)
-                          .ScreenShot ("Save dialog", _out)
-                          .Focus<Button> (b => b.Text == "_Save")
-                          .EnqueueKeyEvent (Key.Enter)
-                          .AssertFalse (sd!.Canceled)
-                          .AssertEqual (GetFileSystemRoot (fs!), sd!.FileName);
+        using GuiTestContext c = With.A (() => NewSaveDialog (out sd, out fs, modal: false), 100, 20, d)
+                                     .ScreenShot ("Save dialog", _out)
+                                     .Focus<Button> (b => b.Text == "_Save")
+                                     .EnqueueKeyEvent (Key.Enter)
+                                     .AssertFalse (sd!.Canceled)
+                                     .AssertEqual (GetFileSystemRoot (fs!), sd!.FileName)
+                                     ;
     }
 
     private string GetFileSystemRoot (IFileSystem fs)

+ 1 - 1
Tests/IntegrationTests/FluentTests/GuiTestContextTests.cs

@@ -18,7 +18,7 @@ public class GuiTestContextTests (ITestOutputHelper outputHelper)
     {
         using var context = new GuiTestContext (d, _out, TimeSpan.FromSeconds (10));
 
-        Assert.NotEqual (Rectangle.Empty, Application.Screen);
+        Assert.NotEqual (Rectangle.Empty, context.App?.Screen);
     }
 
     [Theory]

+ 1 - 1
Tests/StressTests/ScenariosStressTests.cs

@@ -110,7 +110,7 @@ public class ScenariosStressTests
             _output.WriteLine ($"Initialized == {a.Value}");
         }
 
-        void OnApplicationOnIteration (object? s, IterationEventArgs a)
+        void OnApplicationOnIteration (object? s, EventArgs<IApplication?> a)
         {
             iterationCount++;
 

+ 1 - 6
Tests/TerminalGuiFluentTesting/GuiTestContext.Input.cs

@@ -64,7 +64,7 @@ public partial class GuiTestContext
             {
                 mouseEvent.Position = mouseEvent.ScreenPosition;
 
-                app.Driver.InputProcessor.EnqueueMouseEvent (mouseEvent);
+                app.Driver.InputProcessor.EnqueueMouseEvent (app, mouseEvent);
             }
             else
             {
@@ -205,11 +205,6 @@ public partial class GuiTestContext
             App.Driver.EnqueueKeyEvent (key);
             WaitUntil (() => keyReceived);
         }
-        else
-        {
-            Fail ("Expected Application.Driver to be non-null.");
-        }
-
 
         return this;
 

+ 33 - 21
Tests/TerminalGuiFluentTesting/GuiTestContext.cs

@@ -68,7 +68,7 @@ public partial class GuiTestContext : IDisposable
 
         try
         {
-            InitializeApplication ();
+            App?.Init (GetDriverName ());
             _booting.Release ();
 
             // After Init, Application.Screen should be set by the driver
@@ -119,21 +119,36 @@ public partial class GuiTestContext : IDisposable
                              {
                                  try
                                  {
-                                     InitializeApplication ();
-
-                                     _booting.Release ();
-
-                                     Toplevel t = topLevelBuilder ();
-                                     t.Closed += (s, e) => { Finished = true; };
-                                     App?.Run (t); // This will block, but it's on a background thread now
+                                     try
+                                     {
+                                         App?.Init (GetDriverName ());
+                                     }
+                                     catch (Exception e)
+                                     {
+                                         Logging.Error(e.Message);
+                                         _runCancellationTokenSource.Cancel ();
+                                     }
+                                     finally
+                                     {
+                                         _booting.Release ();
+                                     }
 
-                                     t.Dispose ();
-                                     Logging.Trace ("Application.Run completed");
-                                     App?.Shutdown ();
-                                     _runCancellationTokenSource.Cancel ();
+                                     if (App is { Initialized: true })
+                                     {
+                                         Toplevel t = topLevelBuilder ();
+                                         t.Closed += (s, e) => { Finished = true; };
+                                         App?.Run (t); // This will block, but it's on a background thread now
+
+                                         t.Dispose ();
+                                         Logging.Trace ("Application.Run completed");
+                                         App?.Shutdown ();
+                                         _runCancellationTokenSource.Cancel ();
+                                     }
                                  }
                                  catch (OperationCanceledException)
-                                 { }
+                                 {
+                                     Logging.Trace ("OperationCanceledException");
+                                 }
                                  catch (Exception ex)
                                  {
                                      _backgroundException = ex;
@@ -142,7 +157,6 @@ public partial class GuiTestContext : IDisposable
                                  finally
                                  {
                                      CleanupApplication ();
-
                                      if (_logWriter != null)
                                      {
                                          WriteOutLogs (_logWriter);
@@ -165,11 +179,6 @@ public partial class GuiTestContext : IDisposable
         }
     }
 
-    private void InitializeApplication ()
-    {
-        App?.Init (GetDriverName ());
-    }
-
 
     /// <summary>
     ///     Common initialization for both constructors.
@@ -316,7 +325,7 @@ public partial class GuiTestContext : IDisposable
             throw new NotSupportedException ("Cannot WaitIteration during Invoke");
         }
 
-        Logging.Trace ($"WaitIteration started");
+        //Logging.Trace ($"WaitIteration started");
         if (action is null)
         {
             action = (app) => { };
@@ -358,8 +367,9 @@ public partial class GuiTestContext : IDisposable
         GuiTestContext? c = null;
         var sw = Stopwatch.StartNew ();
 
-        //Logging.Trace ($"WaitUntil started with timeout {_timeout}");
+        Logging.Trace ($"WaitUntil started with timeout {_timeout}");
 
+        int count = 0;
         while (!condition ())
         {
             if (sw.Elapsed > _timeout)
@@ -368,8 +378,10 @@ public partial class GuiTestContext : IDisposable
             }
 
             c = WaitIteration ();
+            count++;
         }
 
+        Logging.Trace ($"WaitUntil completed after {sw.ElapsedMilliseconds}ms and {count} iterations");
         return c ?? this;
     }
 

部分文件因为文件数量过多而无法显示