瀏覽代碼

Merge branch 'v2_develop' into copilot/fix-e6dde989-9ea1-4d83-8522-54ed8f70815a

Tig 1 周之前
父節點
當前提交
5a4600e3fe
共有 100 個文件被更改,包括 1853 次插入2097 次删除
  1. 3 16
      .github/workflows/api-docs.yml
  2. 2 11
      Examples/Example/Example.cs
  3. 35 0
      Examples/PowershellExample.ps1
  4. 1 1
      Examples/UICatalog/Scenarios/AnimationScenario/AnimationScenario.cs
  5. 110 121
      Examples/UICatalog/Scenarios/ColorPicker.cs
  6. 1 4
      Examples/UICatalog/Scenarios/CombiningMarks.cs
  7. 1 1
      Examples/UICatalog/Scenarios/ContextMenus.cs
  8. 13 13
      Examples/UICatalog/Scenarios/CsvEditor.cs
  9. 2 2
      Examples/UICatalog/Scenarios/DynamicStatusBar.cs
  10. 9 9
      Examples/UICatalog/Scenarios/Editor.cs
  11. 5 5
      Examples/UICatalog/Scenarios/FileDialogExamples.cs
  12. 1 1
      Examples/UICatalog/Scenarios/Generic.cs
  13. 4 4
      Examples/UICatalog/Scenarios/HexEditor.cs
  14. 20 19
      Examples/UICatalog/Scenarios/Images.cs
  15. 1 1
      Examples/UICatalog/Scenarios/InteractiveTree.cs
  16. 4 4
      Examples/UICatalog/Scenarios/KeyBindings.cs
  17. 7 10
      Examples/UICatalog/Scenarios/LineDrawing.cs
  18. 1 1
      Examples/UICatalog/Scenarios/ListColumns.cs
  19. 14 2
      Examples/UICatalog/Scenarios/Menus.cs
  20. 2 2
      Examples/UICatalog/Scenarios/MessageBoxes.cs
  21. 1 1
      Examples/UICatalog/Scenarios/MultiColouredTable.cs
  22. 1 1
      Examples/UICatalog/Scenarios/Navigation.cs
  23. 2 2
      Examples/UICatalog/Scenarios/Notepad.cs
  24. 1 1
      Examples/UICatalog/Scenarios/RegionScenario.cs
  25. 3 3
      Examples/UICatalog/Scenarios/RuneWidthGreaterThanOne.cs
  26. 1 1
      Examples/UICatalog/Scenarios/Snake.cs
  27. 1 1
      Examples/UICatalog/Scenarios/TextEffectsScenario.cs
  28. 102 24
      Examples/UICatalog/Scenarios/Transparent.cs
  29. 1 1
      Examples/UICatalog/Scenarios/WindowsAndFrameViews.cs
  30. 3 3
      Examples/UICatalog/Scenarios/WizardAsView.cs
  31. 9 5
      Examples/UICatalog/UICatalog.cs
  32. 52 46
      Examples/UICatalog/UICatalogRunnable.cs
  33. 72 36
      README.md
  34. 9 25
      Terminal.Gui/App/Application.Driver.cs
  35. 17 1
      Terminal.Gui/App/Application.Lifecycle.cs
  36. 0 117
      Terminal.Gui/App/Application.cd
  37. 13 9
      Terminal.Gui/App/ApplicationImpl.Driver.cs
  38. 5 25
      Terminal.Gui/App/ApplicationImpl.Lifecycle.cs
  39. 1 1
      Terminal.Gui/App/ApplicationImpl.Run.cs
  40. 28 45
      Terminal.Gui/App/ApplicationImpl.Screen.cs
  41. 2 15
      Terminal.Gui/App/ApplicationImpl.cs
  42. 0 13
      Terminal.Gui/App/IApplication.cs
  43. 12 48
      Terminal.Gui/App/MainLoop/ApplicationMainLoop.cs
  44. 2 8
      Terminal.Gui/App/Mouse/MouseImpl.cs
  45. 32 17
      Terminal.Gui/App/Timeout/TimedEvents.cs
  46. 1 1
      Terminal.Gui/Drawing/Cell.cs
  47. 0 68
      Terminal.Gui/Drawing/Color/AnsiColorNameResolver.cs
  48. 34 50
      Terminal.Gui/Drawing/Color/Color.cs
  49. 6 5
      Terminal.Gui/Drawing/Color/ColorModel.cs
  50. 3 3
      Terminal.Gui/Drawing/Color/ColorQuantizer.cs
  51. 6 53
      Terminal.Gui/Drawing/Color/ColorStrings.cs
  52. 0 83
      Terminal.Gui/Drawing/Color/MultiStandardColorNameResolver.cs
  53. 60 1
      Terminal.Gui/Drawing/Color/StandardColor.cs
  54. 5 2
      Terminal.Gui/Drawing/Color/StandardColors.cs
  55. 23 0
      Terminal.Gui/Drawing/GraphemeHelper.cs
  56. 12 4
      Terminal.Gui/Drawing/LineCanvas/LineCanvas.cs
  57. 5 4
      Terminal.Gui/Drawing/Sixel/SixelSupportDetector.cs
  58. 1 1
      Terminal.Gui/Drivers/DotNetDriver/NetOutput.cs
  59. 31 0
      Terminal.Gui/Drivers/Driver.cs
  60. 181 163
      Terminal.Gui/Drivers/DriverImpl.cs
  61. 16 5
      Terminal.Gui/Drivers/FakeDriver/FakeOutput.cs
  62. 164 123
      Terminal.Gui/Drivers/IDriver.cs
  63. 13 2
      Terminal.Gui/Drivers/IOutput.cs
  64. 1 1
      Terminal.Gui/Drivers/ISizeMonitor.cs
  65. 84 25
      Terminal.Gui/Drivers/OutputBase.cs
  66. 1 1
      Terminal.Gui/Drivers/UnixDriver/UnixOutput.cs
  67. 75 99
      Terminal.Gui/Drivers/WindowsDriver/WindowsOutput.cs
  68. 5 5
      Terminal.Gui/Resources/config.json
  69. 76 26
      Terminal.Gui/Text/TextFormatter.cs
  70. 2 2
      Terminal.Gui/ViewBase/Adornment/Adornment.cs
  71. 1 3
      Terminal.Gui/ViewBase/Adornment/Border.cs
  72. 10 10
      Terminal.Gui/ViewBase/Adornment/Margin.cs
  73. 1 1
      Terminal.Gui/ViewBase/Adornment/ShadowView.cs
  74. 58 6
      Terminal.Gui/ViewBase/DrawContext.cs
  75. 10 10
      Terminal.Gui/ViewBase/Runnable/Runnable.cs
  76. 49 0
      Terminal.Gui/ViewBase/View.Command.cs
  77. 1 1
      Terminal.Gui/ViewBase/View.Content.cs
  78. 98 212
      Terminal.Gui/ViewBase/View.Drawing.cs
  79. 168 0
      Terminal.Gui/ViewBase/View.NeedsDraw.cs
  80. 0 79
      Terminal.Gui/Views/Autocomplete/Autocomplete.cd
  81. 1 1
      Terminal.Gui/Views/Autocomplete/PopupAutocomplete.PopUp.cs
  82. 1 1
      Terminal.Gui/Views/CharMap/CharMap.cs
  83. 0 124
      Terminal.Gui/Views/CollectionNavigation/CollectionNavigation.cd
  84. 0 2
      Terminal.Gui/Views/Color/BBar.cs
  85. 2 5
      Terminal.Gui/Views/Color/ColorBar.cs
  86. 28 45
      Terminal.Gui/Views/Color/ColorModelStrategy.cs
  87. 1 1
      Terminal.Gui/Views/Color/ColorPicker.16.cs
  88. 6 6
      Terminal.Gui/Views/Color/ColorPicker.Prompt.cs
  89. 0 3
      Terminal.Gui/Views/Color/ColorPicker.Style.cs
  90. 1 1
      Terminal.Gui/Views/Color/ColorPicker.cs
  91. 0 2
      Terminal.Gui/Views/Color/GBar.cs
  92. 1 2
      Terminal.Gui/Views/Color/IColorBar.cs
  93. 0 2
      Terminal.Gui/Views/Color/LightnessBar.cs
  94. 0 2
      Terminal.Gui/Views/Color/RBar.cs
  95. 0 2
      Terminal.Gui/Views/Color/SaturationBar.cs
  96. 2 2
      Terminal.Gui/Views/ComboBox.cs
  97. 0 168
      Terminal.Gui/Views/FileDialogs/FileDialog.cd
  98. 1 1
      Terminal.Gui/Views/FileDialogs/FileDialog.cs
  99. 1 1
      Terminal.Gui/Views/GraphView/GraphView.cs
  100. 1 1
      Terminal.Gui/Views/HexView.cs

+ 3 - 16
.github/workflows/api-docs.yml

@@ -1,8 +1,8 @@
-name: Build and publish API docs
+name: Build and publish v2 API docs
 
 
 on:
 on:
   push:
   push:
-    branches: [v1_release, v2_develop]
+    branches: [v2_develop]
 
 
 permissions:
 permissions:
   id-token: write 
   id-token: write 
@@ -10,7 +10,7 @@ permissions:
 
 
 jobs:
 jobs:
   deploy:
   deploy:
-    name: Build and Deploy API docs to github-pages ${{ github.ref_name }}
+    name: Build and Deploy v2 API docs to github-pages ${{ github.ref_name }}
     environment:
     environment:
       name: github-pages
       name: github-pages
       url: ${{ steps.deployment.outputs.page_url }}
       url: ${{ steps.deployment.outputs.page_url }}
@@ -20,7 +20,6 @@ jobs:
       uses: actions/checkout@v4
       uses: actions/checkout@v4
 
 
     - name: DocFX Build
     - name: DocFX Build
-      #if: github.ref_name == 'v1_release' ||  github.ref_name == 'v1_develop'
       working-directory: docfx
       working-directory: docfx
       run: |
       run: |
         dotnet tool install -g docfx
         dotnet tool install -g docfx
@@ -30,27 +29,15 @@ jobs:
       continue-on-error: false
       continue-on-error: false
 
 
     - name: Setup Pages
     - name: Setup Pages
-      #if: github.ref_name == 'v1_release' ||  github.ref_name == 'v1_develop'
       uses: actions/configure-pages@v5
       uses: actions/configure-pages@v5
       
       
     - name: Upload artifact
     - name: Upload artifact
-      #if: github.ref_name == 'v1_release' ||  github.ref_name == 'v1_develop'
       uses: actions/upload-pages-artifact@v3
       uses: actions/upload-pages-artifact@v3
       with:
       with:
         path: docfx/_site
         path: docfx/_site
        
        
     - name: Deploy to GitHub Pages
     - name: Deploy to GitHub Pages
-      if: github.ref_name == 'v2_release' ||  github.ref_name == 'v2_develop'
       id: deployment
       id: deployment
       uses: actions/deploy-pages@v4
       uses: actions/deploy-pages@v4
       with:
       with:
         token: ${{ secrets.GITHUB_TOKEN }}
         token: ${{ secrets.GITHUB_TOKEN }}
-
-    # - name: v1_release Repository Dispatch ${{ github.ref_name }}
-    #   if: github.ref_name == 'v2_develop'
-    #   uses: peter-evans/repository-dispatch@v3
-    #   with:
-    #     token: ${{ secrets.V2DOCS_TOKEN }}
-    #     repository: gui-cs/Terminal.GuiV1Docs
-    #     event-type: v2_develop_push
-    #     client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}"}'

+ 2 - 11
Examples/Example/Example.cs

@@ -8,8 +8,8 @@ using Terminal.Gui.Configuration;
 using Terminal.Gui.ViewBase;
 using Terminal.Gui.ViewBase;
 using Terminal.Gui.Views;
 using Terminal.Gui.Views;
 
 
-// Override the default configuration for the application to use the Light theme
-ConfigurationManager.RuntimeConfig = """{ "Theme": "Light" }""";
+// Override the default configuration for the application to use the Amber Phosphor theme
+ConfigurationManager.RuntimeConfig = """{ "Theme": "Amber Phosphor" }""";
 ConfigurationManager.Enable (ConfigLocations.All);
 ConfigurationManager.Enable (ConfigLocations.All);
 
 
 IApplication app = Application.Create ();
 IApplication app = Application.Create ();
@@ -90,14 +90,5 @@ public sealed class ExampleWindow : Window
 
 
         // Add the views to the Window
         // Add the views to the Window
         Add (usernameLabel, userNameText, passwordLabel, passwordText, btnLogin);
         Add (usernameLabel, userNameText, passwordLabel, passwordText, btnLogin);
-
-        var lv = new ListView
-        {
-            Y = Pos.AnchorEnd (),
-            Height = Dim.Auto (),
-            Width = Dim.Auto ()
-        };
-        lv.SetSource (["One", "Two", "Three", "Four"]);
-        Add (lv);
     }
     }
 }
 }

+ 35 - 0
Examples/PowershellExample.ps1

@@ -0,0 +1,35 @@
+using namespace Terminal.Gui.App        
+using namespace Terminal.Gui.ViewBase
+using namespace Terminal.Gui.Views
+
+$dllFolder = "..\Terminal.Gui\bin\Debug\net8.0"
+
+# For this to work all dependent DLLs need to be in the $dllFolder folder
+# Do this first:
+#   dotnet build -c Debug /p:CopyLocalLockFileAssemblies=true
+
+Get-ChildItem $dllFolder -Filter *.dll | ForEach-Object {
+    Add-Type -Path $_.FullName -ErrorAction SilentlyContinue
+}
+
+$app = [Application]::Create()
+
+$app.Init()
+
+$win = [Window]@{
+    Title  = "Terminal.Gui in Powershell"
+    Width  = [Dim]::Fill()
+    Height = [Dim]::Fill()
+}
+
+$lbl = [Label]@{
+    Text = "Hello from PowerShell + Terminal.Gui!`nPress ESC to quit"
+    X    = [Pos]::Center()
+    Y    = [Pos]::Center()
+}
+$win.Add($lbl)
+
+$app.Run($win)
+
+$win.Dispose()
+$app.Dispose()

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

@@ -173,7 +173,7 @@ public class AnimationScenario : Scenario
         private Rectangle _oldSize = Rectangle.Empty;
         private Rectangle _oldSize = Rectangle.Empty;
         public void NextFrame () { _currentFrame = (_currentFrame + 1) % _frameCount; }
         public void NextFrame () { _currentFrame = (_currentFrame + 1) % _frameCount; }
 
 
-        protected override bool OnDrawingContent ()
+        protected override bool OnDrawingContent (DrawContext? context)
         {
         {
             if (_frameCount == 0)
             if (_frameCount == 0)
             {
             {

+ 110 - 121
Examples/UICatalog/Scenarios/ColorPicker.cs

@@ -1,7 +1,4 @@
-using System;
-using System.Collections.Generic;
-
-namespace UICatalog.Scenarios;
+namespace UICatalog.Scenarios;
 
 
 [ScenarioMetadata ("ColorPicker", "Color Picker and TrueColor demonstration.")]
 [ScenarioMetadata ("ColorPicker", "Color Picker and TrueColor demonstration.")]
 [ScenarioCategory ("Colors")]
 [ScenarioCategory ("Colors")]
@@ -18,25 +15,25 @@ public class ColorPickers : Scenario
     private Label _foregroundColorLabel;
     private Label _foregroundColorLabel;
 
 
     /// <summary>Background ColorPicker.</summary>
     /// <summary>Background ColorPicker.</summary>
-    private ColorPicker backgroundColorPicker;
+    private ColorPicker _backgroundColorPicker;
 
 
     /// <summary>Foreground ColorPicker.</summary>
     /// <summary>Foreground ColorPicker.</summary>
-    private ColorPicker foregroundColorPicker;
+    private ColorPicker _foregroundColorPicker;
 
 
     /// <summary>Background ColorPicker.</summary>
     /// <summary>Background ColorPicker.</summary>
-    private ColorPicker16 backgroundColorPicker16;
+    private ColorPicker16 _backgroundColorPicker16;
 
 
     /// <summary>Foreground ColorPicker.</summary>
     /// <summary>Foreground ColorPicker.</summary>
-    private ColorPicker16 foregroundColorPicker16;
+    private ColorPicker16 _foregroundColorPicker16;
 
 
-    /// <summary>Setup the scenario.</summary>
+    /// <summary>Set up the scenario.</summary>
     public override void Main ()
     public override void Main ()
     {
     {
         Application.Init ();
         Application.Init ();
 
 
         Window app = new ()
         Window app = new ()
         {
         {
-            Title = GetQuitKeyAndName (),
+            Title = GetQuitKeyAndName ()
         };
         };
 
 
         ///////////////////////////////////////
         ///////////////////////////////////////
@@ -44,23 +41,23 @@ public class ColorPickers : Scenario
         ///////////////////////////////////////
         ///////////////////////////////////////
 
 
         // Foreground ColorPicker.
         // Foreground ColorPicker.
-        foregroundColorPicker = new ColorPicker
+        _foregroundColorPicker = new ()
         {
         {
             Title = "_Foreground Color",
             Title = "_Foreground Color",
             BorderStyle = LineStyle.Single,
             BorderStyle = LineStyle.Single,
             Width = Dim.Percent (50)
             Width = Dim.Percent (50)
         };
         };
-        foregroundColorPicker.ColorChanged += ForegroundColor_ColorChanged;
-        app.Add (foregroundColorPicker);
+        _foregroundColorPicker.ColorChanged += ForegroundColor_ColorChanged;
+        app.Add (_foregroundColorPicker);
 
 
-        _foregroundColorLabel = new Label
+        _foregroundColorLabel = new ()
         {
         {
-            X = Pos.Left (foregroundColorPicker), Y = Pos.Bottom (foregroundColorPicker) + 1
+            X = Pos.Left (_foregroundColorPicker), Y = Pos.Bottom (_foregroundColorPicker) + 1
         };
         };
         app.Add (_foregroundColorLabel);
         app.Add (_foregroundColorLabel);
 
 
         // Background ColorPicker.
         // Background ColorPicker.
-        backgroundColorPicker = new ColorPicker
+        _backgroundColorPicker = new ()
         {
         {
             Title = "_Background Color",
             Title = "_Background Color",
             X = Pos.AnchorEnd (),
             X = Pos.AnchorEnd (),
@@ -68,49 +65,47 @@ public class ColorPickers : Scenario
             BorderStyle = LineStyle.Single
             BorderStyle = LineStyle.Single
         };
         };
 
 
-        backgroundColorPicker.ColorChanged += BackgroundColor_ColorChanged;
-        app.Add (backgroundColorPicker);
-        _backgroundColorLabel = new Label ()
+        _backgroundColorPicker.ColorChanged += BackgroundColor_ColorChanged;
+        app.Add (_backgroundColorPicker);
+
+        _backgroundColorLabel = new ()
         {
         {
             X = Pos.AnchorEnd (),
             X = Pos.AnchorEnd (),
-            Y = Pos.Bottom (backgroundColorPicker) + 1
+            Y = Pos.Bottom (_backgroundColorPicker) + 1
         };
         };
 
 
         app.Add (_backgroundColorLabel);
         app.Add (_backgroundColorLabel);
 
 
-
         ///////////////////////////////////////
         ///////////////////////////////////////
         // 16 Color Pickers
         // 16 Color Pickers
         ///////////////////////////////////////
         ///////////////////////////////////////
 
 
-
         // Foreground ColorPicker 16.
         // Foreground ColorPicker 16.
-        foregroundColorPicker16 = new ColorPicker16
+        _foregroundColorPicker16 = new ()
         {
         {
             Title = "_Foreground Color",
             Title = "_Foreground Color",
             BorderStyle = LineStyle.Single,
             BorderStyle = LineStyle.Single,
             Width = Dim.Percent (50),
             Width = Dim.Percent (50),
-            Visible = false  // We default to HSV so hide old one
+            Visible = false // We default to HSV so hide old one
         };
         };
-        foregroundColorPicker16.ColorChanged += ForegroundColor_ColorChanged;
-        app.Add (foregroundColorPicker16);
+        _foregroundColorPicker16.ColorChanged += ForegroundColor_ColorChanged;
+        app.Add (_foregroundColorPicker16);
 
 
         // Background ColorPicker 16.
         // Background ColorPicker 16.
-        backgroundColorPicker16 = new ColorPicker16
+        _backgroundColorPicker16 = new ()
         {
         {
             Title = "_Background Color",
             Title = "_Background Color",
             X = Pos.AnchorEnd (),
             X = Pos.AnchorEnd (),
             Width = Dim.Percent (50),
             Width = Dim.Percent (50),
             BorderStyle = LineStyle.Single,
             BorderStyle = LineStyle.Single,
-            Visible = false  // We default to HSV so hide old one
+            Visible = false // We default to HSV so hide old one
         };
         };
 
 
-        backgroundColorPicker16.ColorChanged += BackgroundColor_ColorChanged;
-        app.Add (backgroundColorPicker16);
-
+        _backgroundColorPicker16.ColorChanged += BackgroundColor_ColorChanged;
+        app.Add (_backgroundColorPicker16);
 
 
         // Demo Label.
         // Demo Label.
-        _demoView = new View
+        _demoView = new ()
         {
         {
             Title = "Color Sample",
             Title = "Color Sample",
             Text = "Lorem Ipsum",
             Text = "Lorem Ipsum",
@@ -124,8 +119,7 @@ public class ColorPickers : Scenario
         };
         };
         app.Add (_demoView);
         app.Add (_demoView);
 
 
-
-        var osColorModel = new OptionSelector ()
+        var osColorModel = new OptionSelector
         {
         {
             Y = Pos.Bottom (_demoView),
             Y = Pos.Bottom (_demoView),
             Width = Dim.Auto (),
             Width = Dim.Auto (),
@@ -137,87 +131,38 @@ public class ColorPickers : Scenario
                 "H_SL",
                 "H_SL",
                 "_16 Colors"
                 "_16 Colors"
             ],
             ],
-            Value = (int)foregroundColorPicker.Style.ColorModel,
+            Value = (int)_foregroundColorPicker.Style.ColorModel
         };
         };
 
 
-        osColorModel.ValueChanged += (_, e) =>
-                                            {
-                                                // 16 colors
-                                                if (e.Value == 3)
-                                                {
-
-                                                    foregroundColorPicker16.Visible = true;
-                                                    foregroundColorPicker.Visible = false;
-
-                                                    backgroundColorPicker16.Visible = true;
-                                                    backgroundColorPicker.Visible = false;
-
-                                                    // Switching to 16 colors
-                                                    ForegroundColor_ColorChanged (null, null);
-                                                    BackgroundColor_ColorChanged (null, null);
-                                                }
-                                                else
-                                                {
-                                                    foregroundColorPicker16.Visible = false;
-                                                    foregroundColorPicker.Visible = true;
-
-                                                    if (e.Value is { })
-                                                    {
-                                                        foregroundColorPicker.Style.ColorModel = (ColorModel)e.Value;
-                                                        foregroundColorPicker.ApplyStyleChanges ();
-
-                                                        backgroundColorPicker16.Visible = false;
-                                                        backgroundColorPicker.Visible = true;
-                                                        backgroundColorPicker.Style.ColorModel = (ColorModel)e.Value;
-                                                    }
-
-                                                    backgroundColorPicker.ApplyStyleChanges ();
-
-
-                                                    // Switching to true colors
-                                                    foregroundColorPicker.SelectedColor = foregroundColorPicker16.SelectedColor;
-                                                    backgroundColorPicker.SelectedColor = backgroundColorPicker16.SelectedColor;
-                                                }
-                                            };
+        osColorModel.ValueChanged += OnOsColorModelOnValueChanged;
 
 
         app.Add (osColorModel);
         app.Add (osColorModel);
 
 
         // Checkbox for switching show text fields on and off
         // Checkbox for switching show text fields on and off
-        var cbShowTextFields = new CheckBox ()
+        var cbShowTextFields = new CheckBox
         {
         {
             Text = "Show _Text Fields",
             Text = "Show _Text Fields",
             Y = Pos.Bottom (osColorModel) + 1,
             Y = Pos.Bottom (osColorModel) + 1,
             Width = Dim.Auto (),
             Width = Dim.Auto (),
             Height = Dim.Auto (),
             Height = Dim.Auto (),
-            CheckedState = foregroundColorPicker.Style.ShowTextFields ? CheckState.Checked : CheckState.UnChecked,
+            CheckedState = _foregroundColorPicker.Style.ShowTextFields ? CheckState.Checked : CheckState.UnChecked
         };
         };
 
 
-        cbShowTextFields.CheckedStateChanging += (_, e) =>
-                                                {
-                                                    foregroundColorPicker.Style.ShowTextFields = e.Result == CheckState.Checked;
-                                                    foregroundColorPicker.ApplyStyleChanges ();
-                                                    backgroundColorPicker.Style.ShowTextFields = e.Result == CheckState.Checked;
-                                                    backgroundColorPicker.ApplyStyleChanges ();
-                                                };
+        cbShowTextFields.CheckedStateChanging += OnCbShowTextFieldsOnCheckedStateChanging;
         app.Add (cbShowTextFields);
         app.Add (cbShowTextFields);
 
 
         // Checkbox for switching show text fields on and off
         // Checkbox for switching show text fields on and off
-        var cbShowName = new CheckBox ()
+        var cbShowName = new CheckBox
         {
         {
             Text = "Show Color _Name",
             Text = "Show Color _Name",
             Y = Pos.Bottom (cbShowTextFields) + 1,
             Y = Pos.Bottom (cbShowTextFields) + 1,
             Width = Dim.Auto (),
             Width = Dim.Auto (),
             Height = Dim.Auto (),
             Height = Dim.Auto (),
-            CheckedState = foregroundColorPicker.Style.ShowColorName ? CheckState.Checked : CheckState.UnChecked,
+            CheckedState = _foregroundColorPicker.Style.ShowColorName ? CheckState.Checked : CheckState.UnChecked
         };
         };
 
 
-        cbShowName.CheckedStateChanging += (_, e) =>
-                                           {
-                                               foregroundColorPicker.Style.ShowColorName = e.Result == CheckState.Checked;
-                                               foregroundColorPicker.ApplyStyleChanges ();
-                                               backgroundColorPicker.Style.ShowColorName = e.Result == CheckState.Checked;
-                                               backgroundColorPicker.ApplyStyleChanges ();
-                                           };
+        cbShowName.CheckedStateChanging += OnCbShowTextFieldsOnCheckedStateChanging;
+
         app.Add (cbShowName);
         app.Add (cbShowName);
 
 
         var lblDriverName = new Label
         var lblDriverName = new Label
@@ -241,47 +186,94 @@ public class ColorPickers : Scenario
         {
         {
             X = Pos.Right (cbSupportsTrueColor) + 1,
             X = Pos.Right (cbSupportsTrueColor) + 1,
             Y = Pos.Top (lblDriverName),
             Y = Pos.Top (lblDriverName),
-            CheckedState = Application.Force16Colors ? CheckState.Checked : CheckState.UnChecked,
+            CheckedState = Application.Driver.Force16Colors ? CheckState.Checked : CheckState.UnChecked,
             Enabled = canTrueColor,
             Enabled = canTrueColor,
             Text = "Force16Colors"
             Text = "Force16Colors"
         };
         };
-        cbUseTrueColor.CheckedStateChanging += (_, evt) => { Application.Force16Colors = evt.Result == CheckState.Checked; };
+        cbUseTrueColor.CheckedStateChanging += (_, evt) => { Application.Driver!.Force16Colors = evt.Result == CheckState.Checked; };
         app.Add (lblDriverName, cbSupportsTrueColor, cbUseTrueColor);
         app.Add (lblDriverName, cbSupportsTrueColor, cbUseTrueColor);
+
         // Set default colors.
         // Set default colors.
-        foregroundColorPicker.SelectedColor = _demoView.SuperView!.GetAttributeForRole (VisualRole.Normal).Foreground.GetClosestNamedColor16 ();
-        backgroundColorPicker.SelectedColor = _demoView.SuperView.GetAttributeForRole (VisualRole.Normal).Background.GetClosestNamedColor16 ();
+        _foregroundColorPicker.SelectedColor = _demoView.SuperView!.GetAttributeForRole (VisualRole.Normal).Foreground.GetClosestNamedColor16 ();
+        _backgroundColorPicker.SelectedColor = _demoView.SuperView.GetAttributeForRole (VisualRole.Normal).Background.GetClosestNamedColor16 ();
 
 
         Application.Run (app);
         Application.Run (app);
         app.Dispose ();
         app.Dispose ();
         Application.Shutdown ();
         Application.Shutdown ();
+
+        return;
+
+        void OnCbShowTextFieldsOnCheckedStateChanging (object _, ResultEventArgs<CheckState> e)
+        {
+            _foregroundColorPicker.Style.ShowTextFields = e.Result == CheckState.Checked;
+            _foregroundColorPicker.ApplyStyleChanges ();
+            _backgroundColorPicker.Style.ShowTextFields = e.Result == CheckState.Checked;
+            _backgroundColorPicker.ApplyStyleChanges ();
+        }
+
+        void OnOsColorModelOnValueChanged (object _, EventArgs<int?> e)
+        {
+            // 16 colors
+            if (e.Value == 3)
+            {
+                _foregroundColorPicker16.Visible = true;
+                _foregroundColorPicker.Visible = false;
+
+                _backgroundColorPicker16.Visible = true;
+                _backgroundColorPicker.Visible = false;
+
+                // Switching to 16 colors
+                ForegroundColor_ColorChanged (null, null);
+                BackgroundColor_ColorChanged (null, null);
+            }
+            else
+            {
+                _foregroundColorPicker16.Visible = false;
+                _foregroundColorPicker.Visible = true;
+
+                if (e.Value is { })
+                {
+                    _foregroundColorPicker.Style.ColorModel = (ColorModel)e.Value;
+                    _foregroundColorPicker.ApplyStyleChanges ();
+
+                    _backgroundColorPicker16.Visible = false;
+                    _backgroundColorPicker.Visible = true;
+                    _backgroundColorPicker.Style.ColorModel = (ColorModel)e.Value;
+                }
+
+                _backgroundColorPicker.ApplyStyleChanges ();
+
+                // Switching to true colors
+                _foregroundColorPicker.SelectedColor = _foregroundColorPicker16.SelectedColor;
+                _backgroundColorPicker.SelectedColor = _backgroundColorPicker16.SelectedColor;
+            }
+        }
     }
     }
 
 
     /// <summary>Fired when background color is changed.</summary>
     /// <summary>Fired when background color is changed.</summary>
     private void BackgroundColor_ColorChanged (object sender, ResultEventArgs<Color> e)
     private void BackgroundColor_ColorChanged (object sender, ResultEventArgs<Color> e)
     {
     {
-        UpdateColorLabel (_backgroundColorLabel,
-                          backgroundColorPicker.Visible ?
-                              backgroundColorPicker.SelectedColor :
-                              backgroundColorPicker16.SelectedColor
-                          );
+        UpdateColorLabel (
+                          _backgroundColorLabel,
+                          _backgroundColorPicker.Visible ? _backgroundColorPicker.SelectedColor : _backgroundColorPicker16.SelectedColor
+                         );
         UpdateDemoLabel ();
         UpdateDemoLabel ();
     }
     }
 
 
     /// <summary>Fired when foreground color is changed.</summary>
     /// <summary>Fired when foreground color is changed.</summary>
     private void ForegroundColor_ColorChanged (object sender, ResultEventArgs<Color> e)
     private void ForegroundColor_ColorChanged (object sender, ResultEventArgs<Color> e)
     {
     {
-        UpdateColorLabel (_foregroundColorLabel,
-                          foregroundColorPicker.Visible ?
-                                 foregroundColorPicker.SelectedColor :
-                                 foregroundColorPicker16.SelectedColor
-                          );
+        UpdateColorLabel (
+                          _foregroundColorLabel,
+                          _foregroundColorPicker.Visible ? _foregroundColorPicker.SelectedColor : _foregroundColorPicker16.SelectedColor
+                         );
         UpdateDemoLabel ();
         UpdateDemoLabel ();
     }
     }
 
 
     /// <summary>Update a color label from his ColorPicker.</summary>
     /// <summary>Update a color label from his ColorPicker.</summary>
     private void UpdateColorLabel (Label label, Color color)
     private void UpdateColorLabel (Label label, Color color)
     {
     {
-        label.ClearViewport (null);
+        label.ClearViewport ();
 
 
         label.Text =
         label.Text =
             $"{color} ({(int)color}) #{color.R:X2}{color.G:X2}{color.B:X2}";
             $"{color} ({(int)color}) #{color.R:X2}{color.G:X2}{color.B:X2}";
@@ -290,17 +282,14 @@ public class ColorPickers : Scenario
     /// <summary>Update Demo Label.</summary>
     /// <summary>Update Demo Label.</summary>
     private void UpdateDemoLabel ()
     private void UpdateDemoLabel ()
     {
     {
-        _demoView.SetScheme (new Scheme
-        {
-            Normal = new Attribute (
-                                    foregroundColorPicker.Visible ?
-                                        foregroundColorPicker.SelectedColor :
-                                        foregroundColorPicker16.SelectedColor,
-                                    backgroundColorPicker.Visible ?
-                                        backgroundColorPicker.SelectedColor :
-                                        backgroundColorPicker16.SelectedColor
-                                   )
-        });
+        _demoView.SetScheme (
+                             new ()
+                             {
+                                 Normal = new (
+                                               _foregroundColorPicker.Visible ? _foregroundColorPicker.SelectedColor : _foregroundColorPicker16.SelectedColor,
+                                               _backgroundColorPicker.Visible ? _backgroundColorPicker.SelectedColor : _backgroundColorPicker16.SelectedColor
+                                              )
+                             });
     }
     }
 
 
     public override List<Key> GetDemoKeyStrokes ()
     public override List<Key> GetDemoKeyStrokes ()
@@ -310,7 +299,7 @@ public class ColorPickers : Scenario
             Key.B.WithAlt
             Key.B.WithAlt
         ];
         ];
 
 
-        for (int i = 0; i < 200; i++)
+        for (var i = 0; i < 200; i++)
         {
         {
             keys.Add (Key.CursorRight);
             keys.Add (Key.CursorRight);
         }
         }
@@ -318,7 +307,7 @@ public class ColorPickers : Scenario
         keys.Add (Key.Tab);
         keys.Add (Key.Tab);
         keys.Add (Key.Tab);
         keys.Add (Key.Tab);
 
 
-        for (int i = 0; i < 200; i++)
+        for (var i = 0; i < 200; i++)
         {
         {
             keys.Add (Key.CursorLeft);
             keys.Add (Key.CursorLeft);
         }
         }
@@ -326,7 +315,7 @@ public class ColorPickers : Scenario
         keys.Add (Key.Tab);
         keys.Add (Key.Tab);
         keys.Add (Key.Tab);
         keys.Add (Key.Tab);
 
 
-        for (int i = 0; i < 200; i++)
+        for (var i = 0; i < 200; i++)
         {
         {
             keys.Add (Key.CursorLeft);
             keys.Add (Key.CursorLeft);
         }
         }

+ 1 - 4
Examples/UICatalog/Scenarios/CombiningMarks.cs

@@ -10,11 +10,8 @@ public class CombiningMarks : Scenario
         Application.Init ();
         Application.Init ();
         var top = new Runnable ();
         var top = new Runnable ();
 
 
-        top.DrawComplete += (s, e) =>
+        top.DrawingContent += (s, e) =>
         {
         {
-            // Forces reset _lineColsOffset because we're dealing with direct draw
-            Application.TopRunnableView!.SetNeedsDraw ();
-
             var i = -1;
             var i = -1;
             top.Move (0, ++i);
             top.Move (0, ++i);
             top.AddStr ("Terminal.Gui supports all combining sequences that can be rendered as an unique grapheme.");
             top.AddStr ("Terminal.Gui supports all combining sequences that can be rendered as an unique grapheme.");

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

@@ -49,7 +49,7 @@ public class ContextMenus : Scenario
             var text = "Context Menu";
             var text = "Context Menu";
             var width = 20;
             var width = 20;
 
 
-            CreateWinContextMenu (ApplicationImpl.Instance);
+            CreateWinContextMenu (Application.Instance);
 
 
             var label = new Label
             var label = new Label
             {
             {

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

@@ -215,7 +215,7 @@ public class CsvEditor : Scenario
                                       _tableView.Table.Columns
                                       _tableView.Table.Columns
                                      );
                                      );
 
 
-            int? result = MessageBox.Query (ApplicationImpl.Instance,
+            int? result = MessageBox.Query (Application.Instance,
                                            "Column Type",
                                            "Column Type",
                                            "Pick a data type for the column",
                                            "Pick a data type for the column",
                                            "Date",
                                            "Date",
@@ -308,7 +308,7 @@ public class CsvEditor : Scenario
 
 
         if (_tableView.SelectedColumn == -1)
         if (_tableView.SelectedColumn == -1)
         {
         {
-            MessageBox.ErrorQuery (ApplicationImpl.Instance, "No Column", "No column selected", "Ok");
+            MessageBox.ErrorQuery (Application.Instance, "No Column", "No column selected", "Ok");
 
 
             return;
             return;
         }
         }
@@ -320,7 +320,7 @@ public class CsvEditor : Scenario
         }
         }
         catch (Exception ex)
         catch (Exception ex)
         {
         {
-            MessageBox.ErrorQuery (ApplicationImpl.Instance, "Could not remove column", ex.Message, "Ok");
+            MessageBox.ErrorQuery (Application.Instance, "Could not remove column", ex.Message, "Ok");
         }
         }
     }
     }
 
 
@@ -342,7 +342,7 @@ public class CsvEditor : Scenario
             }
             }
             catch (Exception ex)
             catch (Exception ex)
             {
             {
-                MessageBox.ErrorQuery (ApplicationImpl.Instance, 60, 20, "Failed to set text", ex.Message, "Ok");
+                MessageBox.ErrorQuery (Application.Instance, 60, 20, "Failed to set text", ex.Message, "Ok");
             }
             }
 
 
             _tableView.Update ();
             _tableView.Update ();
@@ -388,7 +388,7 @@ public class CsvEditor : Scenario
 
 
         if (_tableView.SelectedColumn == -1)
         if (_tableView.SelectedColumn == -1)
         {
         {
-            MessageBox.ErrorQuery (ApplicationImpl.Instance, "No Column", "No column selected", "Ok");
+            MessageBox.ErrorQuery (Application.Instance, "No Column", "No column selected", "Ok");
 
 
             return;
             return;
         }
         }
@@ -413,7 +413,7 @@ public class CsvEditor : Scenario
         }
         }
         catch (Exception ex)
         catch (Exception ex)
         {
         {
-            MessageBox.ErrorQuery (ApplicationImpl.Instance, "Error moving column", ex.Message, "Ok");
+            MessageBox.ErrorQuery (Application.Instance, "Error moving column", ex.Message, "Ok");
         }
         }
     }
     }
 
 
@@ -426,7 +426,7 @@ public class CsvEditor : Scenario
 
 
         if (_tableView.SelectedRow == -1)
         if (_tableView.SelectedRow == -1)
         {
         {
-            MessageBox.ErrorQuery (ApplicationImpl.Instance, "No Rows", "No row selected", "Ok");
+            MessageBox.ErrorQuery (Application.Instance, "No Rows", "No row selected", "Ok");
 
 
             return;
             return;
         }
         }
@@ -462,7 +462,7 @@ public class CsvEditor : Scenario
         }
         }
         catch (Exception ex)
         catch (Exception ex)
         {
         {
-            MessageBox.ErrorQuery (ApplicationImpl.Instance, "Error moving column", ex.Message, "Ok");
+            MessageBox.ErrorQuery (Application.Instance, "Error moving column", ex.Message, "Ok");
         }
         }
     }
     }
 
 
@@ -470,7 +470,7 @@ public class CsvEditor : Scenario
     {
     {
         if (_tableView?.Table is null)
         if (_tableView?.Table is null)
         {
         {
-            MessageBox.ErrorQuery (ApplicationImpl.Instance, "No Table Loaded", "No table has currently be opened", "Ok");
+            MessageBox.ErrorQuery (Application.Instance, "No Table Loaded", "No table has currently be opened", "Ok");
 
 
             return true;
             return true;
         }
         }
@@ -582,7 +582,7 @@ public class CsvEditor : Scenario
         }
         }
         catch (Exception ex)
         catch (Exception ex)
         {
         {
-            MessageBox.ErrorQuery (ApplicationImpl.Instance,
+            MessageBox.ErrorQuery (Application.Instance,
                                    "Open Failed",
                                    "Open Failed",
                                    $"Error on line {lineNumber}{Environment.NewLine}{ex.Message}",
                                    $"Error on line {lineNumber}{Environment.NewLine}{ex.Message}",
                                    "Ok"
                                    "Ok"
@@ -612,7 +612,7 @@ public class CsvEditor : Scenario
     {
     {
         if (_tableView?.Table is null || string.IsNullOrWhiteSpace (_currentFile) || _currentTable is null)
         if (_tableView?.Table is null || string.IsNullOrWhiteSpace (_currentFile) || _currentTable is null)
         {
         {
-            MessageBox.ErrorQuery (ApplicationImpl.Instance, "No file loaded", "No file is currently loaded", "Ok");
+            MessageBox.ErrorQuery (Application.Instance, "No file loaded", "No file is currently loaded", "Ok");
 
 
             return;
             return;
         }
         }
@@ -674,7 +674,7 @@ public class CsvEditor : Scenario
 
 
         if (col.DataType == typeof (string))
         if (col.DataType == typeof (string))
         {
         {
-            MessageBox.ErrorQuery (ApplicationImpl.Instance,
+            MessageBox.ErrorQuery (Application.Instance,
                                    "Cannot Format Column",
                                    "Cannot Format Column",
                                    "String columns cannot be Formatted, try adding a new column to the table with a date/numerical Type",
                                    "String columns cannot be Formatted, try adding a new column to the table with a date/numerical Type",
                                    "Ok"
                                    "Ok"
@@ -711,7 +711,7 @@ public class CsvEditor : Scenario
 
 
         if (_tableView.SelectedColumn == -1)
         if (_tableView.SelectedColumn == -1)
         {
         {
-            MessageBox.ErrorQuery (ApplicationImpl.Instance, "No Column", "No column selected", "Ok");
+            MessageBox.ErrorQuery (Application.Instance, "No Column", "No column selected", "Ok");
 
 
             return;
             return;
         }
         }

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

@@ -79,7 +79,7 @@ public class DynamicStatusBar : Scenario
             }
             }
             catch (Exception ex)
             catch (Exception ex)
             {
             {
-                MessageBox.ErrorQuery (ApplicationImpl.Instance, "Binding Error", $"Binding failed: {ex}.", "Ok");
+                MessageBox.ErrorQuery (Application.Instance, "Binding Error", $"Binding failed: {ex}.", "Ok");
             }
             }
         }
         }
     }
     }
@@ -140,7 +140,7 @@ public class DynamicStatusBar : Scenario
         public TextView TextAction { get; }
         public TextView TextAction { get; }
         public TextField TextShortcut { get; }
         public TextField TextShortcut { get; }
         public TextField TextTitle { get; }
         public TextField TextTitle { get; }
-        public Action CreateAction (DynamicStatusItem item) { return () => MessageBox.ErrorQuery (ApplicationImpl.Instance, item.Title, item.Action, "Ok"); }
+        public Action CreateAction (DynamicStatusItem item) { return () => MessageBox.ErrorQuery (Application.Instance, item.Title, item.Action, "Ok"); }
 
 
         public void EditStatusItem (Shortcut statusItem)
         public void EditStatusItem (Shortcut statusItem)
         {
         {

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

@@ -201,7 +201,7 @@ public class Editor : Scenario
         Debug.Assert (_textView.IsDirty);
         Debug.Assert (_textView.IsDirty);
 
 
         int? r = MessageBox.ErrorQuery (
         int? r = MessageBox.ErrorQuery (
-                                       ApplicationImpl.Instance,
+                                       Application.Instance,
                                        "Save File",
                                        "Save File",
                                        $"Do you want save changes in {_appWindow.Title}?",
                                        $"Do you want save changes in {_appWindow.Title}?",
                                        "Yes",
                                        "Yes",
@@ -236,7 +236,7 @@ public class Editor : Scenario
         }
         }
         catch (Exception ex)
         catch (Exception ex)
         {
         {
-            MessageBox.ErrorQuery (ApplicationImpl.Instance, "Error", ex.Message, "Ok");
+            MessageBox.ErrorQuery (Application.Instance, "Error", ex.Message, "Ok");
         }
         }
     }
     }
 
 
@@ -315,11 +315,11 @@ public class Editor : Scenario
 
 
         if (!found)
         if (!found)
         {
         {
-            MessageBox.Query (ApplicationImpl.Instance, "Find", $"The following specified text was not found: '{_textToFind}'", "Ok");
+            MessageBox.Query (Application.Instance, "Find", $"The following specified text was not found: '{_textToFind}'", "Ok");
         }
         }
         else if (gaveFullTurn)
         else if (gaveFullTurn)
         {
         {
-            MessageBox.Query (ApplicationImpl.Instance,
+            MessageBox.Query (Application.Instance,
                               "Find",
                               "Find",
                               $"No more occurrences were found for the following specified text: '{_textToFind}'",
                               $"No more occurrences were found for the following specified text: '{_textToFind}'",
                               "Ok"
                               "Ok"
@@ -895,7 +895,7 @@ public class Editor : Scenario
 
 
         if (_textView.ReplaceAllText (_textToFind, _matchCase, _matchWholeWord, _textToReplace))
         if (_textView.ReplaceAllText (_textToFind, _matchCase, _matchWholeWord, _textToReplace))
         {
         {
-            MessageBox.Query (ApplicationImpl.Instance,
+            MessageBox.Query (Application.Instance,
                               "Replace All",
                               "Replace All",
                               $"All occurrences were replaced for the following specified text: '{_textToReplace}'",
                               $"All occurrences were replaced for the following specified text: '{_textToReplace}'",
                               "Ok"
                               "Ok"
@@ -903,7 +903,7 @@ public class Editor : Scenario
         }
         }
         else
         else
         {
         {
-            MessageBox.Query (ApplicationImpl.Instance,
+            MessageBox.Query (Application.Instance,
                               "Replace All",
                               "Replace All",
                               $"None of the following specified text was found: '{_textToFind}'",
                               $"None of the following specified text was found: '{_textToFind}'",
                               "Ok"
                               "Ok"
@@ -1155,7 +1155,7 @@ public class Editor : Scenario
         {
         {
             if (File.Exists (path))
             if (File.Exists (path))
             {
             {
-                if (MessageBox.Query (ApplicationImpl.Instance,
+                if (MessageBox.Query (Application.Instance,
                                       "Save File",
                                       "Save File",
                                       "File already exists. Overwrite any way?",
                                       "File already exists. Overwrite any way?",
                                       "No",
                                       "No",
@@ -1194,11 +1194,11 @@ public class Editor : Scenario
             _originalText = Encoding.Unicode.GetBytes (_textView.Text);
             _originalText = Encoding.Unicode.GetBytes (_textView.Text);
             _saved = true;
             _saved = true;
             _textView.ClearHistoryChanges ();
             _textView.ClearHistoryChanges ();
-            MessageBox.Query (ApplicationImpl.Instance, "Save File", "File was successfully saved.", "Ok");
+            MessageBox.Query (Application.Instance, "Save File", "File was successfully saved.", "Ok");
         }
         }
         catch (Exception ex)
         catch (Exception ex)
         {
         {
-            MessageBox.ErrorQuery (ApplicationImpl.Instance, "Error", ex.Message, "Ok");
+            MessageBox.ErrorQuery (Application.Instance, "Error", ex.Message, "Ok");
 
 
             return false;
             return false;
         }
         }

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

@@ -133,7 +133,7 @@ public class FileDialogExamples : Scenario
                              }
                              }
                              catch (Exception ex)
                              catch (Exception ex)
                              {
                              {
-                                 MessageBox.ErrorQuery (ApplicationImpl.Instance, "Error", ex.ToString (), "_Ok");
+                                 MessageBox.ErrorQuery (Application.Instance, "Error", ex.ToString (), "_Ok");
                              }
                              }
                              finally
                              finally
                              {
                              {
@@ -153,7 +153,7 @@ public class FileDialogExamples : Scenario
         {
         {
             if (File.Exists (e.Dialog.Path))
             if (File.Exists (e.Dialog.Path))
             {
             {
-                int? result = MessageBox.Query (ApplicationImpl.Instance, "Overwrite?", "File already exists", "_Yes", "_No");
+                int? result = MessageBox.Query (Application.Instance, "Overwrite?", "File already exists", "_Yes", "_No");
                 e.Cancel = result == 1;
                 e.Cancel = result == 1;
             }
             }
         }
         }
@@ -248,7 +248,7 @@ public class FileDialogExamples : Scenario
 
 
             if (canceled)
             if (canceled)
             {
             {
-                MessageBox.Query (ApplicationImpl.Instance,
+                MessageBox.Query (Application.Instance,
                                   "Canceled",
                                   "Canceled",
                                   "You canceled navigation and did not pick anything",
                                   "You canceled navigation and did not pick anything",
                                   "Ok"
                                   "Ok"
@@ -256,7 +256,7 @@ public class FileDialogExamples : Scenario
             }
             }
             else if (_cbAllowMultipleSelection.CheckedState == CheckState.Checked)
             else if (_cbAllowMultipleSelection.CheckedState == CheckState.Checked)
             {
             {
-                MessageBox.Query (ApplicationImpl.Instance,
+                MessageBox.Query (Application.Instance,
                                   "Chosen!",
                                   "Chosen!",
                                   "You chose:" + Environment.NewLine + string.Join (Environment.NewLine, multiSelected.Select (m => m)),
                                   "You chose:" + Environment.NewLine + string.Join (Environment.NewLine, multiSelected.Select (m => m)),
                                   "Ok"
                                   "Ok"
@@ -264,7 +264,7 @@ public class FileDialogExamples : Scenario
             }
             }
             else
             else
             {
             {
-                MessageBox.Query (ApplicationImpl.Instance,
+                MessageBox.Query (Application.Instance,
                                   "Chosen!",
                                   "Chosen!",
                                   "You chose:" + Environment.NewLine + path,
                                   "You chose:" + Environment.NewLine + path,
                                   "Ok"
                                   "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.
                                 // When Accepting is handled, set e.Handled to true to prevent further processing.
                                 e.Handled = true;
                                 e.Handled = true;
-                                MessageBox.ErrorQuery (ApplicationImpl.Instance, "Error", "You pressed the button!", "_Ok");
+                                MessageBox.ErrorQuery (Application.Instance, "Error", "You pressed the button!", "_Ok");
                             };
                             };
 
 
         appWindow.Add (button);
         appWindow.Add (button);

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

@@ -181,7 +181,7 @@ public class HexEditor : Scenario
         }
         }
     }
     }
 
 
-    private void Copy () { MessageBox.ErrorQuery (ApplicationImpl.Instance, "Not Implemented", "Functionality not yet implemented.", "Ok"); }
+    private void Copy () { MessageBox.ErrorQuery (Application.Instance, "Not Implemented", "Functionality not yet implemented.", "Ok"); }
 
 
     private void CreateDemoFile (string fileName)
     private void CreateDemoFile (string fileName)
     {
     {
@@ -208,7 +208,7 @@ public class HexEditor : Scenario
         ms.Close ();
         ms.Close ();
     }
     }
 
 
-    private void Cut () { MessageBox.ErrorQuery (ApplicationImpl.Instance, "Not Implemented", "Functionality not yet implemented.", "Ok"); }
+    private void Cut () { MessageBox.ErrorQuery (Application.Instance, "Not Implemented", "Functionality not yet implemented.", "Ok"); }
 
 
     private Stream LoadFile ()
     private Stream LoadFile ()
     {
     {
@@ -216,7 +216,7 @@ public class HexEditor : Scenario
 
 
         if (!_saved && _hexView!.Edits.Count > 0 && _hexView.Source is {})
         if (!_saved && _hexView!.Edits.Count > 0 && _hexView.Source is {})
         {
         {
-            if (MessageBox.ErrorQuery (ApplicationImpl.Instance,
+            if (MessageBox.ErrorQuery (Application.Instance,
                                        "Save",
                                        "Save",
                                        "The changes were not saved. Want to open without saving?",
                                        "The changes were not saved. Want to open without saving?",
                                        "_Yes",
                                        "_Yes",
@@ -267,7 +267,7 @@ public class HexEditor : Scenario
         d.Dispose ();
         d.Dispose ();
     }
     }
 
 
-    private void Paste () { MessageBox.ErrorQuery (ApplicationImpl.Instance, "Not Implemented", "Functionality not yet implemented.", "_Ok"); }
+    private void Paste () { MessageBox.ErrorQuery (Application.Instance, "Not Implemented", "Functionality not yet implemented.", "_Ok"); }
     private void Quit () { Application.RequestStop (); }
     private void Quit () { Application.RequestStop (); }
 
 
     private void Save ()
     private void Save ()

+ 20 - 19
Examples/UICatalog/Scenarios/Images.cs

@@ -122,11 +122,11 @@ public class Images : Scenario
         {
         {
             X = Pos.Right (cbSupportsTrueColor) + 2,
             X = Pos.Right (cbSupportsTrueColor) + 2,
             Y = 0,
             Y = 0,
-            CheckedState = !Application.Force16Colors ? CheckState.Checked : CheckState.UnChecked,
+            CheckedState = !Driver.Force16Colors ? CheckState.Checked : CheckState.UnChecked,
             Enabled = canTrueColor,
             Enabled = canTrueColor,
             Text = "Use true color"
             Text = "Use true color"
         };
         };
-        cbUseTrueColor.CheckedStateChanging += (_, evt) => Application.Force16Colors = evt.Result == CheckState.UnChecked;
+        cbUseTrueColor.CheckedStateChanging += (_, evt) => Driver.Force16Colors = evt.Result == CheckState.UnChecked;
         _win.Add (cbUseTrueColor);
         _win.Add (cbUseTrueColor);
 
 
         var btnOpenImage = new Button { X = Pos.Right (cbUseTrueColor) + 2, Y = 0, Text = "Open Image" };
         var btnOpenImage = new Button { X = Pos.Right (cbUseTrueColor) + 2, Y = 0, Text = "Open Image" };
@@ -183,7 +183,7 @@ public class Images : Scenario
 
 
         if (!_sixelSupportResult.SupportsTransparency)
         if (!_sixelSupportResult.SupportsTransparency)
         {
         {
-            if (MessageBox.Query (ApplicationImpl.Instance,
+            if (MessageBox.Query (Application.Instance,
                                   "Transparency Not Supported",
                                   "Transparency Not Supported",
                                   "It looks like your terminal does not support transparent sixel backgrounds. Do you want to try anyway?",
                                   "It looks like your terminal does not support transparent sixel backgrounds. Do you want to try anyway?",
                                   "Yes",
                                   "Yes",
@@ -219,18 +219,21 @@ public class Images : Scenario
         Color [,] bmp = _fire.GetFirePixels ();
         Color [,] bmp = _fire.GetFirePixels ();
 
 
         // TODO: Static way of doing this, suboptimal
         // TODO: Static way of doing this, suboptimal
-        if (_fireSixel != null)
+        // ConcurrentQueue doesn't support Remove, so we update the existing object
+        if (_fireSixel == null)
         {
         {
-            Application.Sixel.Remove (_fireSixel);
+            _fireSixel = new ()
+            {
+                SixelData = _fireEncoder.EncodeSixel (bmp),
+                ScreenPosition = new (0, 0)
+            };
+            Application.GetSixels ().Enqueue (_fireSixel);
         }
         }
-
-        _fireSixel = new ()
+        else
         {
         {
-            SixelData = _fireEncoder.EncodeSixel (bmp),
-            ScreenPosition = new (0, 0)
-        };
-
-        Application.Sixel.Add (_fireSixel);
+            _fireSixel.SixelData = _fireEncoder.EncodeSixel (bmp);
+            _fireSixel.ScreenPosition = new (0, 0);
+        }
 
 
         _win.SetNeedsDraw ();
         _win.SetNeedsDraw ();
 
 
@@ -245,8 +248,6 @@ public class Images : Scenario
         _sixelNotSupported.Dispose ();
         _sixelNotSupported.Dispose ();
         _sixelSupported.Dispose ();
         _sixelSupported.Dispose ();
         _isDisposed = true;
         _isDisposed = true;
-
-        Application.Sixel.Clear ();
     }
     }
 
 
     private void OpenImage (object sender, CommandEventArgs e)
     private void OpenImage (object sender, CommandEventArgs e)
@@ -288,7 +289,7 @@ public class Images : Scenario
         }
         }
         catch (Exception ex)
         catch (Exception ex)
         {
         {
-            MessageBox.ErrorQuery (ApplicationImpl.Instance, "Could not open file", ex.Message, "Ok");
+            MessageBox.ErrorQuery (Application.Instance, "Could not open file", ex.Message, "Ok");
 
 
             return;
             return;
         }
         }
@@ -492,7 +493,7 @@ public class Images : Scenario
     {
     {
         if (_imageView.FullResImage == null)
         if (_imageView.FullResImage == null)
         {
         {
-            MessageBox.Query (ApplicationImpl.Instance, "No Image Loaded", "You must first open an image.  Use the 'Open Image' button above.", "Ok");
+            MessageBox.Query (Application.Instance, "No Image Loaded", "You must first open an image.  Use the 'Open Image' button above.", "Ok");
 
 
             return;
             return;
         }
         }
@@ -513,7 +514,7 @@ public class Images : Scenario
                 ScreenPosition = _screenLocationForSixel
                 ScreenPosition = _screenLocationForSixel
             };
             };
 
 
-            Application.Sixel.Add (_sixelImage);
+            Application.GetSixels ().Enqueue (_sixelImage);
         }
         }
         else
         else
         {
         {
@@ -631,7 +632,7 @@ public class Images : Scenario
         public Image<Rgba32> FullResImage;
         public Image<Rgba32> FullResImage;
         private Image<Rgba32> _matchSize;
         private Image<Rgba32> _matchSize;
 
 
-        protected override bool OnDrawingContent ()
+        protected override bool OnDrawingContent (DrawContext context)
         {
         {
             if (FullResImage == null)
             if (FullResImage == null)
             {
             {
@@ -708,7 +709,7 @@ public class Images : Scenario
             return (columns, rows);
             return (columns, rows);
         }
         }
 
 
-        protected override bool OnDrawingContent ()
+        protected override bool OnDrawingContent (DrawContext context)
         {
         {
             if (_palette == null || _palette.Count == 0)
             if (_palette == null || _palette.Count == 0)
             {
             {

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

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

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

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

+ 7 - 10
Examples/UICatalog/Scenarios/LineDrawing.cs

@@ -1,7 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
+using System.Text;
 
 
 namespace UICatalog.Scenarios;
 namespace UICatalog.Scenarios;
 
 
@@ -136,14 +133,14 @@ public class LineDrawing : Scenario
         var d = new Dialog
         var d = new Dialog
         {
         {
             Title = title,
             Title = title,
-            Width = Application.Force16Colors ? 35 : Dim.Auto (DimAutoStyle.Auto, Dim.Percent (80), Dim.Percent (90)),
+            Width = Driver.Force16Colors ? 35 : Dim.Auto (DimAutoStyle.Auto, Dim.Percent (80), Dim.Percent (90)),
             Height = 10
             Height = 10
         };
         };
 
 
         var btnOk = new Button
         var btnOk = new Button
         {
         {
             X = Pos.Center () - 5,
             X = Pos.Center () - 5,
-            Y = Application.Force16Colors ? 6 : 4,
+            Y = Driver.Force16Colors ? 6 : 4,
             Text = "Ok",
             Text = "Ok",
             Width = Dim.Auto (),
             Width = Dim.Auto (),
             IsDefault = true
             IsDefault = true
@@ -177,7 +174,7 @@ public class LineDrawing : Scenario
         d.AddButton (btnCancel);
         d.AddButton (btnCancel);
 
 
         View cp;
         View cp;
-        if (Application.Force16Colors)
+        if (Driver.Force16Colors)
         {
         {
             cp = new ColorPicker16
             cp = new ColorPicker16
             {
             {
@@ -200,7 +197,7 @@ public class LineDrawing : Scenario
 
 
         Application.Run (d);
         Application.Run (d);
         d.Dispose ();
         d.Dispose ();
-        newColor = Application.Force16Colors ? ((ColorPicker16)cp).SelectedColor : ((ColorPicker)cp).SelectedColor;
+        newColor = Driver.Force16Colors ? ((ColorPicker16)cp).SelectedColor : ((ColorPicker)cp).SelectedColor;
 
 
         return accept;
         return accept;
     }
     }
@@ -273,7 +270,7 @@ public class DrawingArea : View
     public ITool CurrentTool { get; set; } = new DrawLineTool ();
     public ITool CurrentTool { get; set; } = new DrawLineTool ();
     public DrawingArea () { AddLayer (); }
     public DrawingArea () { AddLayer (); }
 
 
-    protected override bool OnDrawingContent ()
+    protected override bool OnDrawingContent (DrawContext context)
     {
     {
         foreach (LineCanvas canvas in Layers)
         foreach (LineCanvas canvas in Layers)
         {
         {
@@ -380,7 +377,7 @@ public class AttributeView : View
     }
     }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    protected override bool OnDrawingContent ()
+    protected override bool OnDrawingContent (DrawContext context)
     {
     {
         Color fg = Value.Foreground;
         Color fg = Value.Foreground;
         Color bg = Value.Background;
         Color bg = Value.Background;

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

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

+ 14 - 2
Examples/UICatalog/Scenarios/Menus.cs

@@ -253,7 +253,13 @@ public class Menus : Scenario
 
 
             // The source of truth is our status CB; any time it changes, update the menu item
             // The source of truth is our status CB; any time it changes, update the menu item
             var enableOverwriteMenuItemCb = menuBar.GetMenuItemsWithTitle ("Overwrite").FirstOrDefault ()?.CommandView as CheckBox;
             var enableOverwriteMenuItemCb = menuBar.GetMenuItemsWithTitle ("Overwrite").FirstOrDefault ()?.CommandView as CheckBox;
-            enableOverwriteStatusCb.CheckedStateChanged += (_, _) => enableOverwriteMenuItemCb!.CheckedState = enableOverwriteStatusCb.CheckedState;
+            enableOverwriteStatusCb.CheckedStateChanged += (_, _) =>
+                                                           {
+                                                               if (enableOverwriteMenuItemCb is { })
+                                                               {
+                                                                   enableOverwriteMenuItemCb.CheckedState = enableOverwriteStatusCb.CheckedState;
+                                                               }
+                                                           };
 
 
             menuBar.Accepted += (o, args) =>
             menuBar.Accepted += (o, args) =>
                                 {
                                 {
@@ -298,7 +304,13 @@ public class Menus : Scenario
 
 
             // The source of truth is our status CB; any time it changes, update the menu item
             // The source of truth is our status CB; any time it changes, update the menu item
             var editModeMenuItemCb = menuBar.GetMenuItemsWithTitle ("EditMode").FirstOrDefault ()?.CommandView as CheckBox;
             var editModeMenuItemCb = menuBar.GetMenuItemsWithTitle ("EditMode").FirstOrDefault ()?.CommandView as CheckBox;
-            editModeStatusCb.CheckedStateChanged += (_, _) => editModeMenuItemCb!.CheckedState = editModeStatusCb.CheckedState;
+            editModeStatusCb.CheckedStateChanged += (_, _) =>
+                                                       {
+                                                           if (editModeMenuItemCb is { })
+                                                           {
+                                                               editModeMenuItemCb.CheckedState = editModeStatusCb.CheckedState;
+                                                           }
+                                                       };
 
 
             menuBar.Accepted += (o, args) =>
             menuBar.Accepted += (o, args) =>
                                 {
                                 {

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

@@ -251,7 +251,7 @@ public class MessageBoxes : Scenario
                                                {
                                                {
                                                    buttonPressedLabel.Text =
                                                    buttonPressedLabel.Text =
                                                        $"{MessageBox.Query (
                                                        $"{MessageBox.Query (
-                                                                            ApplicationImpl.Instance, width,
+                                                                            Application.Instance, width,
                                                                              height,
                                                                              height,
                                                                              titleEdit.Text,
                                                                              titleEdit.Text,
                                                                              messageEdit.Text,
                                                                              messageEdit.Text,
@@ -263,7 +263,7 @@ public class MessageBoxes : Scenario
                                                else
                                                else
                                                {
                                                {
                                                    buttonPressedLabel.Text =
                                                    buttonPressedLabel.Text =
-                                                       $"{MessageBox.ErrorQuery (ApplicationImpl.Instance,
+                                                       $"{MessageBox.ErrorQuery (Application.Instance,
                                                                                  width,
                                                                                  width,
                                                                                  height,
                                                                                  height,
                                                                                  titleEdit.Text,
                                                                                  titleEdit.Text,

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

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

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

@@ -59,7 +59,7 @@ public class Navigation : Scenario
             Y = 0,
             Y = 0,
             Title = $"TopButton _{GetNextHotKey ()}"
             Title = $"TopButton _{GetNextHotKey ()}"
         };
         };
-        button.Accepting += (sender, args) => MessageBox.Query (ApplicationImpl.Instance, "hi", button.Title, "_Ok");
+        button.Accepting += (sender, args) => MessageBox.Query (Application.Instance, "hi", button.Title, "_Ok");
 
 
         testFrame.Add (button);
         testFrame.Add (button);
 
 

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

@@ -71,7 +71,7 @@ public class Notepad : Scenario
                                        new MenuItem
                                        new MenuItem
                                        {
                                        {
                                            Title = "_About",
                                            Title = "_About",
-                                           Action = () => MessageBox.Query (ApplicationImpl.Instance,  "Notepad", "About Notepad...", "Ok")
+                                           Action = () => MessageBox.Query (Application.Instance,  "Notepad", "About Notepad...", "Ok")
                                        }
                                        }
                                    ]
                                    ]
                                   )
                                   )
@@ -196,7 +196,7 @@ public class Notepad : Scenario
 
 
         if (tab.UnsavedChanges)
         if (tab.UnsavedChanges)
         {
         {
-            int? result = MessageBox.Query (ApplicationImpl.Instance,
+            int? result = MessageBox.Query (Application.Instance,
                                             "Save Changes",
                                             "Save Changes",
                                             $"Save changes to {tab.Text.TrimEnd ('*')}",
                                             $"Save changes to {tab.Text.TrimEnd ('*')}",
                                             "Yes",
                                             "Yes",

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

@@ -268,7 +268,7 @@ public class AttributeView : View
     }
     }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    protected override bool OnDrawingContent ()
+    protected override bool OnDrawingContent (DrawContext? context)
     {
     {
         Color fg = Value?.Foreground ?? Color.Black;
         Color fg = Value?.Foreground ?? Color.Black;
         Color bg = Value?.Background ?? Color.Black;
         Color bg = Value?.Background ?? Color.Black;

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

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

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

@@ -322,7 +322,7 @@ public class Snake : Scenario
 
 
         private SnakeState State { get; }
         private SnakeState State { get; }
 
 
-        protected override bool OnDrawingContent ()
+        protected override bool OnDrawingContent (DrawContext context)
         {
         {
             SetAttribute (white);
             SetAttribute (white);
             ClearViewport ();
             ClearViewport ();

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

@@ -109,7 +109,7 @@ internal class GradientsView : View
     private const int LABEL_HEIGHT = 1;
     private const int LABEL_HEIGHT = 1;
     private const int GRADIENT_WITH_LABEL_HEIGHT = GRADIENT_HEIGHT + LABEL_HEIGHT + 1; // +1 for spacing
     private const int GRADIENT_WITH_LABEL_HEIGHT = GRADIENT_HEIGHT + LABEL_HEIGHT + 1; // +1 for spacing
 
 
-    protected override bool OnDrawingContent ()
+    protected override bool OnDrawingContent (DrawContext context)
     {
     {
         DrawTopLineGradient (Viewport);
         DrawTopLineGradient (Viewport);
 
 

+ 102 - 24
Examples/UICatalog/Scenarios/Transparent.cs

@@ -1,8 +1,9 @@
+// ReSharper disable AccessToDisposedClosure
 #nullable enable
 #nullable enable
 
 
 namespace UICatalog.Scenarios;
 namespace UICatalog.Scenarios;
 
 
-[ScenarioMetadata ("Transparent", "Testing Transparency")]
+[ScenarioMetadata ("Transparent", "Demonstrates View Transparency")]
 public sealed class Transparent : Scenario
 public sealed class Transparent : Scenario
 {
 {
     public override void Main ()
     public override void Main ()
@@ -53,11 +54,19 @@ public sealed class Transparent : Scenario
 
 
         var tv = new TransparentView ()
         var tv = new TransparentView ()
         {
         {
-            X = 3,
-            Y = 3,
-            Width = 50,
-            Height = 15
+            X = 2,
+            Y = 2,
+            Width = Dim.Fill (10),
+            Height = Dim.Fill (10)
         };
         };
+
+        appWindow.ViewportChanged += (sender, args) =>
+                                      {
+                                          // Little hack to convert the Dim.Fill to actual size
+                                          // So resizing works
+                                          tv.Width = appWindow!.Frame.Width - 10;
+                                          tv.Height = appWindow!.Frame.Height - 10;
+                                      };
         appWindow.Add (tv);
         appWindow.Add (tv);
 
 
         // Run - Start the application.
         // Run - Start the application.
@@ -72,34 +81,31 @@ public sealed class Transparent : Scenario
     {
     {
         public TransparentView ()
         public TransparentView ()
         {
         {
-            Title = "Transparent View";
-            //base.Text = "View.Text.\nThis should be opaque.\nNote how clipping works?";
-            TextFormatter.Alignment = Alignment.Center;
-            TextFormatter.VerticalAlignment = Alignment.Center;
+            Title = "Transparent View - Move and Resize To See Transparency In Action";
+            base.Text = "View.Text.\nThis should be opaque. Note how clipping works?";
             Arrangement = ViewArrangement.Overlapped | ViewArrangement.Resizable | ViewArrangement.Movable;
             Arrangement = ViewArrangement.Overlapped | ViewArrangement.Resizable | ViewArrangement.Movable;
-            ViewportSettings |= Terminal.Gui.ViewBase.ViewportSettingsFlags.Transparent | Terminal.Gui.ViewBase.ViewportSettingsFlags.TransparentMouse;
+            ViewportSettings |= ViewportSettingsFlags.Transparent | ViewportSettingsFlags.TransparentMouse;
             BorderStyle = LineStyle.RoundedDotted;
             BorderStyle = LineStyle.RoundedDotted;
-            //SchemeName = "Base";
+            SchemeName = "Base";
 
 
             var transparentSubView = new View ()
             var transparentSubView = new View ()
             {
             {
-                Text = "Sizable/Movable View with border. Should be opaque. No Shadow.",
+                Text = "Sizable/Movable SubView with border and shadow.",
                 Id = "transparentSubView",
                 Id = "transparentSubView",
-                X = 4,
-                Y = 8,
+                X = Pos.Center (),
+                Y = Pos.Center (),
                 Width = 20,
                 Width = 20,
                 Height = 8,
                 Height = 8,
                 BorderStyle = LineStyle.Dashed,
                 BorderStyle = LineStyle.Dashed,
                 Arrangement = ViewArrangement.Movable | ViewArrangement.Resizable,
                 Arrangement = ViewArrangement.Movable | ViewArrangement.Resizable,
-                // ShadowStyle = ShadowStyle.Transparent,
+                ShadowStyle = ShadowStyle.Transparent,
             };
             };
             transparentSubView.Border!.Thickness = new (1, 1, 1, 1);
             transparentSubView.Border!.Thickness = new (1, 1, 1, 1);
             transparentSubView.SchemeName = "Dialog";
             transparentSubView.SchemeName = "Dialog";
-            //transparentSubView.Visible = false;
 
 
             Button button = new Button ()
             Button button = new Button ()
             {
             {
-                Title = "_Opaque Shadows No Worky",
+                Title = "_Opaque Shadow",
                 X = Pos.Center (),
                 X = Pos.Center (),
                 Y = 2,
                 Y = 2,
                 SchemeName = "Dialog",
                 SchemeName = "Dialog",
@@ -109,8 +115,6 @@ public sealed class Transparent : Scenario
                                     MessageBox.Query (App, "Clicked!", "Button in Transparent View", "_Ok");
                                     MessageBox.Query (App, "Clicked!", "Button in Transparent View", "_Ok");
                                     args.Handled = true;
                                     args.Handled = true;
                                 };
                                 };
-            //button.Visible = false;
-
 
 
             var shortcut = new Shortcut ()
             var shortcut = new Shortcut ()
             {
             {
@@ -121,7 +125,6 @@ public sealed class Transparent : Scenario
                 HelpText = "Help!",
                 HelpText = "Help!",
                 Key = Key.F11,
                 Key = Key.F11,
                 SchemeName = "Base"
                 SchemeName = "Base"
-
             };
             };
 
 
             button.ClearingViewport += (sender, args) =>
             button.ClearingViewport += (sender, args) =>
@@ -129,16 +132,91 @@ public sealed class Transparent : Scenario
                                            args.Cancel = true;
                                            args.Cancel = true;
                                        };
                                        };
 
 
+            // Subscribe to DrawingContent event to draw "TUI" 
+            DrawingContent += TransparentView_DrawingContent;
 
 
             base.Add (button);
             base.Add (button);
             base.Add (shortcut);
             base.Add (shortcut);
             base.Add (transparentSubView);
             base.Add (transparentSubView);
 
 
-            //Padding.Thickness = new (1);
-            //Padding.SchemeName = "Error";
+            Padding!.Thickness = new (1);
+            Padding.Text = "This is the Padding";
+        }
+
+        private void TransparentView_DrawingContent (object? sender, DrawEventArgs e)
+        {
+            // Draw "TUI" text using rectangular regions, positioned after "Hi"
+            // Letter "T"
+            Rectangle tTop = new (20, 5, 7, 2);      // Top horizontal bar
+            Rectangle tStem = new (23, 7, 2, 8);     // Vertical stem
+
+            // Letter "U"
+            Rectangle uLeft = new (30, 5, 2, 8);     // Left vertical bar
+            Rectangle uBottom = new (32, 13, 3, 2);  // Bottom horizontal bar
+            Rectangle uRight = new (35, 5, 2, 8);    // Right vertical bar
+
+            // Letter "I"
+            Rectangle iTop = new (39, 5, 4, 2);      // Bar on top
+            Rectangle iStem = new (40, 7, 2, 6);     // Vertical stem
+            Rectangle iBottom = new (39, 13, 4, 2);      // Bar on Bottom
+
+            // Draw "TUI" using the HotActive attribute
+            SetAttributeForRole (VisualRole.HotActive);
+            FillRect (tTop, Glyphs.BlackCircle);
+            FillRect (tStem, Glyphs.BlackCircle);
+            FillRect (uLeft, Glyphs.BlackCircle);
+            FillRect (uBottom, Glyphs.BlackCircle);
+            FillRect (uRight, Glyphs.BlackCircle);
+            FillRect (iTop, Glyphs.BlackCircle);
+            FillRect (iStem, Glyphs.BlackCircle);
+            FillRect (iBottom, Glyphs.BlackCircle);
+
+            Region tuiRegion = new Region (ViewportToScreen (tTop));
+            tuiRegion.Union (ViewportToScreen (tStem));
+            tuiRegion.Union (ViewportToScreen (uLeft));
+            tuiRegion.Union (ViewportToScreen (uBottom));
+            tuiRegion.Union (ViewportToScreen (uRight));
+            tuiRegion.Union (ViewportToScreen (iTop));
+            tuiRegion.Union (ViewportToScreen (iStem));
+            tuiRegion.Union (ViewportToScreen (iBottom));
+
+            // Register the drawn region for "TUI" to enable transparency effects
+            e.DrawContext?.AddDrawnRegion (tuiRegion);
+        }
 
 
-            Margin!.Thickness = new (1);
-           // Margin.ViewportSettings |= Terminal.Gui.ViewportSettingsFlags.Transparent;
+        /// <inheritdoc />
+        protected override bool OnDrawingContent (DrawContext? context)
+        {
+            base.OnDrawingContent (context);
+
+            // Draw "Hi" text using rectangular regions
+            // Letter "H"
+            Rectangle hLeft = new (5, 5, 2, 10);      // Left vertical bar
+            Rectangle hMiddle = new (7, 9, 3, 2);     // Middle horizontal bar
+            Rectangle hRight = new (10, 5, 2, 10);    // Right vertical bar
+
+            // Letter "i" (with some space between H and i)
+            Rectangle iDot = new (15, 5, 2, 2);       // Dot on top
+            Rectangle iStem = new (15, 9, 2, 6);      // Vertical stem
+
+            // Draw "Hi" using the Highlight attribute
+            SetAttributeForRole (VisualRole.Highlight);
+            FillRect (hLeft, Glyphs.BlackCircle);
+            FillRect (hMiddle, Glyphs.BlackCircle);
+            FillRect (hRight, Glyphs.BlackCircle);
+            FillRect (iDot, Glyphs.BlackCircle);
+            FillRect (iStem, Glyphs.BlackCircle);
+
+            // Register the drawn region for "Hi" to enable transparency effects
+            Region hiRegion = new Region (ViewportToScreen (hLeft));
+            hiRegion.Union (ViewportToScreen (hMiddle));
+            hiRegion.Union (ViewportToScreen (hRight));
+            hiRegion.Union (ViewportToScreen (iDot));
+            hiRegion.Union (ViewportToScreen (iStem));
+            context?.AddDrawnRegion (hiRegion);
+
+            // Return false to allow DrawingContent event to fire
+            return false;
         }
         }
 
 
         /// <inheritdoc />
         /// <inheritdoc />

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

@@ -18,7 +18,7 @@ public class WindowsAndFrameViews : Scenario
 
 
         static int? About ()
         static int? About ()
         {
         {
-            return MessageBox.Query (ApplicationImpl.Instance,
+            return MessageBox.Query (Application.Instance,
                                      "About UI Catalog",
                                      "About UI Catalog",
                                      "UI Catalog is a comprehensive sample library for Terminal.Gui",
                                      "UI Catalog is a comprehensive sample library for Terminal.Gui",
                                      "Ok"
                                      "Ok"

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

@@ -21,7 +21,7 @@ public class WizardAsView : Scenario
                                        {
                                        {
                                            Title = "_Restart Configuration...",
                                            Title = "_Restart Configuration...",
                                            Action = () => MessageBox.Query (
                                            Action = () => MessageBox.Query (
-                                                                            ApplicationImpl.Instance,
+                                                                            Application.Instance,
                                                                             "Wizard",
                                                                             "Wizard",
                                                                             "Are you sure you want to reset the Wizard and start over?",
                                                                             "Are you sure you want to reset the Wizard and start over?",
                                                                             "Ok",
                                                                             "Ok",
@@ -32,7 +32,7 @@ public class WizardAsView : Scenario
                                        {
                                        {
                                            Title = "Re_boot Server...",
                                            Title = "Re_boot Server...",
                                            Action = () => MessageBox.Query (
                                            Action = () => MessageBox.Query (
-                                                                            ApplicationImpl.Instance,
+                                                                            Application.Instance,
                                                                             "Wizard",
                                                                             "Wizard",
                                                                             "Are you sure you want to reboot the server start over?",
                                                                             "Are you sure you want to reboot the server start over?",
                                                                             "Ok",
                                                                             "Ok",
@@ -43,7 +43,7 @@ public class WizardAsView : Scenario
                                        {
                                        {
                                            Title = "_Shutdown Server...",
                                            Title = "_Shutdown Server...",
                                            Action = () => MessageBox.Query (
                                            Action = () => MessageBox.Query (
-                                                                            ApplicationImpl.Instance,
+                                                                            Application.Instance,
                                                                             "Wizard",
                                                                             "Wizard",
                                                                             "Are you sure you want to cancel setup and shutdown?",
                                                                             "Are you sure you want to cancel setup and shutdown?",
                                                                             "Ok",
                                                                             "Ok",

+ 9 - 5
Examples/UICatalog/UICatalog.cs

@@ -196,7 +196,7 @@ public class UICatalog
 
 
         UICatalogMain (Options);
         UICatalogMain (Options);
 
 
-        Debug.Assert (Application.ForceDriver == string.Empty);
+        Application.ForceDriver = string.Empty;
 
 
         return 0;
         return 0;
     }
     }
@@ -433,8 +433,10 @@ public class UICatalog
 
 
             // This call to Application.Shutdown brackets the Application.Init call
             // This call to Application.Shutdown brackets the Application.Init call
             // made by Scenario.Init() above
             // made by Scenario.Init() above
-            // TODO: Throw if shutdown was not called already
-            Application.Shutdown ();
+            if (Application.Driver is { })
+            {
+                Application.Shutdown ();
+            }
 
 
             VerifyObjectsWereDisposed ();
             VerifyObjectsWereDisposed ();
 
 
@@ -482,8 +484,10 @@ public class UICatalog
 
 
         scenario.Dispose ();
         scenario.Dispose ();
 
 
-        // TODO: Throw if shutdown was not called already
-        Application.Shutdown ();
+        if (Application.Driver is { })
+        {
+            Application.Shutdown ();
+        }
 
 
         return results;
         return results;
     }
     }

+ 52 - 46
Examples/UICatalog/UICatalogRunnable.cs

@@ -43,9 +43,12 @@ public class UICatalogRunnable : Runnable
         IsRunningChanged += IsRunningChangedHandler;
         IsRunningChanged += IsRunningChangedHandler;
 
 
         // Restore previous selections
         // Restore previous selections
-        if (_categoryList.Source?.Count > 0) {
+        if (_categoryList.Source?.Count > 0)
+        {
             _categoryList.SelectedItem = _cachedCategoryIndex ?? 0;
             _categoryList.SelectedItem = _cachedCategoryIndex ?? 0;
-        } else {
+        }
+        else
+        {
             _categoryList.SelectedItem = null;
             _categoryList.SelectedItem = null;
         }
         }
         _scenarioList.SelectedRow = _cachedScenarioIndex;
         _scenarioList.SelectedRow = _cachedScenarioIndex;
@@ -176,7 +179,7 @@ public class UICatalogRunnable : Runnable
             _force16ColorsMenuItemCb = new ()
             _force16ColorsMenuItemCb = new ()
             {
             {
                 Title = "Force _16 Colors",
                 Title = "Force _16 Colors",
-                CheckedState = Application.Force16Colors ? CheckState.Checked : CheckState.UnChecked,
+                CheckedState = Application.Driver!.Force16Colors ? CheckState.Checked : CheckState.UnChecked,
                 // Best practice for CheckBoxes in menus is to disable focus and highlight states
                 // Best practice for CheckBoxes in menus is to disable focus and highlight states
                 CanFocus = false,
                 CanFocus = false,
                 HighlightStates = MouseState.None
                 HighlightStates = MouseState.None
@@ -184,7 +187,7 @@ public class UICatalogRunnable : Runnable
 
 
             _force16ColorsMenuItemCb.CheckedStateChanging += (sender, args) =>
             _force16ColorsMenuItemCb.CheckedStateChanging += (sender, args) =>
                                                              {
                                                              {
-                                                                 if (Application.Force16Colors
+                                                                 if (Application.Driver!.Force16Colors
                                                                      && args.Result == CheckState.UnChecked
                                                                      && args.Result == CheckState.UnChecked
                                                                      && !Application.Driver!.SupportsTrueColor)
                                                                      && !Application.Driver!.SupportsTrueColor)
                                                                  {
                                                                  {
@@ -194,10 +197,10 @@ public class UICatalogRunnable : Runnable
 
 
             _force16ColorsMenuItemCb.CheckedStateChanged += (sender, args) =>
             _force16ColorsMenuItemCb.CheckedStateChanged += (sender, args) =>
                                                             {
                                                             {
-                                                                Application.Force16Colors = args.Value == CheckState.Checked;
+                                                                Application.Driver!.Force16Colors = args.Value == CheckState.Checked;
 
 
                                                                 _force16ColorsShortcutCb!.CheckedState = args.Value;
                                                                 _force16ColorsShortcutCb!.CheckedState = args.Value;
-                                                                Application.LayoutAndDraw ();
+                                                                SetNeedsDraw ();
                                                             };
                                                             };
 
 
             menuItems.Add (
             menuItems.Add (
@@ -295,18 +298,25 @@ public class UICatalogRunnable : Runnable
             _diagnosticFlagsSelector.UsedHotKeys.Add (Key.D);
             _diagnosticFlagsSelector.UsedHotKeys.Add (Key.D);
             _diagnosticFlagsSelector.AssignHotKeys = true;
             _diagnosticFlagsSelector.AssignHotKeys = true;
             _diagnosticFlagsSelector.Value = Diagnostics;
             _diagnosticFlagsSelector.Value = Diagnostics;
-            _diagnosticFlagsSelector.ValueChanged += (sender, args) =>
-                                                     {
-                                                         _diagnosticFlags = (ViewDiagnosticFlags)_diagnosticFlagsSelector.Value;
-                                                         Diagnostics = _diagnosticFlags;
-                                                     };
+            _diagnosticFlagsSelector.Selecting += (sender, args) =>
+                                                  {
+                                                      _diagnosticFlags = (ViewDiagnosticFlags)((int)args.Context!.Source!.Data!);// (ViewDiagnosticFlags)_diagnosticFlagsSelector.Value;
+                                                      Diagnostics = _diagnosticFlags;
+                                                  };
 
 
-            menuItems.Add (
-                           new MenuItem
-                           {
-                               CommandView = _diagnosticFlagsSelector,
-                               HelpText = "View Diagnostics"
-                           });
+            MenuItem diagFlagMenuItem = new MenuItem ()
+            {
+                CommandView = _diagnosticFlagsSelector,
+                HelpText = "View Diagnostics"
+            };
+            diagFlagMenuItem.Accepting += (sender, args) =>
+                                         {
+                                             //_diagnosticFlags = (ViewDiagnosticFlags)_diagnosticFlagsSelector.Value;
+                                             //Diagnostics = _diagnosticFlags;
+                                             //args.Handled = true;
+                                         };
+
+            menuItems.Add (diagFlagMenuItem);
 
 
             menuItems.Add (new Line ());
             menuItems.Add (new Line ());
 
 
@@ -319,8 +329,13 @@ public class UICatalogRunnable : Runnable
                 HighlightStates = MouseState.None
                 HighlightStates = MouseState.None
             };
             };
 
 
-            _disableMouseCb.CheckedStateChanged += (_, args) => { Application.IsMouseDisabled = args.Value == CheckState.Checked; };
+            //_disableMouseCb.CheckedStateChanged += (_, args) => { Application.IsMouseDisabled = args.Value == CheckState.Checked; };
+            _disableMouseCb.Selecting += (sender, args) =>
+                                         {
+                                             Application.IsMouseDisabled = !Application.IsMouseDisabled;
+                                             _disableMouseCb.CheckedState = Application.IsMouseDisabled ? CheckState.Checked : CheckState.None;
 
 
+                                         };
             menuItems.Add (
             menuItems.Add (
                            new MenuItem
                            new MenuItem
                            {
                            {
@@ -639,39 +654,30 @@ public class UICatalogRunnable : Runnable
         _force16ColorsShortcutCb = new ()
         _force16ColorsShortcutCb = new ()
         {
         {
             Title = "16 color mode",
             Title = "16 color mode",
-            CheckedState = Application.Force16Colors ? CheckState.Checked : CheckState.UnChecked,
-            CanFocus = false
+            CheckedState = Application.Driver!.Force16Colors ? CheckState.Checked : CheckState.UnChecked,
+            CanFocus = true
         };
         };
 
 
-        _force16ColorsShortcutCb.CheckedStateChanging += (sender, args) =>
-                                                         {
-                                                             if (Application.Force16Colors
-                                                                 && args.Result == CheckState.UnChecked
-                                                                 && !Application.Driver!.SupportsTrueColor)
-                                                             {
-                                                                 // If the driver does not support TrueColor, we cannot disable 16 colors
-                                                                 args.Handled = true;
-                                                             }
-                                                         };
-
-        _force16ColorsShortcutCb.CheckedStateChanged += (sender, args) =>
-                                                         {
-                                                             Application.Force16Colors = args.Value == CheckState.Checked;
-                                                             _force16ColorsMenuItemCb!.CheckedState = args.Value;
-                                                             Application.LayoutAndDraw ();
-                                                         };
+        Shortcut force16ColorsShortcut = new ()
+        {
+            CanFocus = false,
+            CommandView = _force16ColorsShortcutCb,
+            HelpText = "",
+            BindKeyToApplication = true,
+            Key = Key.F7
+        };
 
 
+        force16ColorsShortcut.Accepting += (sender, args) =>
+                                           {
+                                               Application.Driver.Force16Colors = !Application.Driver.Force16Colors;
+                                               _force16ColorsMenuItemCb!.CheckedState = Application.Driver.Force16Colors ? CheckState.Checked : CheckState.UnChecked;
+                                               SetNeedsDraw ();
+                                               args.Handled = true;
+                                           };
         statusBar.Add (
         statusBar.Add (
                        _shQuit,
                        _shQuit,
                        statusBarShortcut,
                        statusBarShortcut,
-                       new Shortcut
-                       {
-                           CanFocus = false,
-                           CommandView = _force16ColorsShortcutCb,
-                           HelpText = "",
-                           BindKeyToApplication = true,
-                           Key = Key.F7
-                       },
+                       force16ColorsShortcut,
                        _shVersion
                        _shVersion
                       );
                       );
 
 
@@ -707,7 +713,7 @@ public class UICatalogRunnable : Runnable
         }
         }
 
 
         _disableMouseCb!.CheckedState = Application.IsMouseDisabled ? CheckState.Checked : CheckState.UnChecked;
         _disableMouseCb!.CheckedState = Application.IsMouseDisabled ? CheckState.Checked : CheckState.UnChecked;
-        _force16ColorsShortcutCb!.CheckedState = Application.Force16Colors ? CheckState.Checked : CheckState.UnChecked;
+        _force16ColorsShortcutCb!.CheckedState = Application.Driver!.Force16Colors ? CheckState.Checked : CheckState.UnChecked;
 
 
         Application.TopRunnableView?.SetNeedsDraw ();
         Application.TopRunnableView?.SetNeedsDraw ();
     }
     }

+ 72 - 36
README.md

@@ -6,27 +6,23 @@
 
 
 # Terminal.Gui v2
 # Terminal.Gui v2
 
 
-The premier toolkit for building rich console apps for Windows, the Mac, and Linux/Unix.
+Cross-platform UI toolkit for building sophisticated terminal UI (TUI) applications on Windows, macOS, and Linux/Unix.
 
 
 ![logo](docfx/images/logo.png)
 ![logo](docfx/images/logo.png)
 
 
-* The current, stable, release of Terminal.Gui v1 is [![Version](https://img.shields.io/nuget/v/Terminal.Gui.svg)](https://www.nuget.org/packages/Terminal.Gui).
+* **v2 Alpha** (Current): ![NuGet Version](https://img.shields.io/nuget/vpre/Terminal.Gui) - Recommended for new projects
+* **v1 (Legacy)**: [![Version](https://img.shields.io/nuget/v/Terminal.Gui.svg)](https://www.nuget.org/packages/Terminal.Gui) - Maintenance mode only
 
 
-> :warning: **Note:**  
-> `v1` is in maintenance mode and we will only accept PRs for issues impacting existing functionality.
-
-* The current `Alpha` release of Terminal.Gui v2 is ![NuGet Version](https://img.shields.io/nuget/vpre/Terminal.Gui)
-
-> :warning: **Note:**  
-> Developers starting new TUI projects are encouraged to target `v2 Alpha`. The API is significantly changed, and significantly improved. There will be breaking changes in the API before Beta, but the core API is stable.
+> **Important:**
+> - **v1** is in maintenance mode - only critical bug fixes accepted
+> - **v2 Alpha** is recommended for new projects - API is stable with comprehensive features
+> - Breaking changes possible before Beta, but core architecture is solid
 
 
 ![Sample app](docfx/images/sample.gif)
 ![Sample app](docfx/images/sample.gif)
 
 
 # Quick Start
 # Quick Start
 
 
-Paste these commands into your favorite terminal on Windows, Mac, or Linux. This will install the [Terminal.Gui.Templates](https://github.com/gui-cs/Terminal.Gui.templates), create a new "Hello World" TUI app, and run it.
-
-(Press `CTRL-Q` to exit the app)
+Install the [Terminal.Gui.Templates](https://github.com/gui-cs/Terminal.Gui.templates), create a new TUI app, and run it:
 
 
 ```powershell
 ```powershell
 dotnet new --install Terminal.Gui.templates
 dotnet new --install Terminal.Gui.templates
@@ -35,60 +31,100 @@ cd myproj
 dotnet run
 dotnet run
 ```
 ```
 
 
-To run the UICatalog demo app that shows all the controls and features of the toolkit, use the following command:
+Press `Esc` to exit (the default [QuitKey](https://gui-cs.github.io/Terminal.Gui/api/Terminal.Gui.App.Application.html#Terminal_Gui_App_Application_QuitKey)).
+
+Run the comprehensive [UI Catalog](Examples/UICatalog) demo to explore all controls:
 
 
 ```powershell
 ```powershell
 dotnet run --project Examples/UICatalog/UICatalog.csproj
 dotnet run --project Examples/UICatalog/UICatalog.csproj
 ```
 ```
 
 
-There is also a [visual designer](https://github.com/gui-cs/TerminalGuiDesigner) (uses Terminal.Gui itself).
+# Simple Example
+
+```csharp
+using Terminal.Gui;
+
+using IApplication app = Application.Create ();
+app.Init ();
+
+using Window window = new () { Title = "Hello World (Esc to quit)" };
+Label label = new ()
+{
+    Text = "Hello, Terminal.Gui v2!",
+    X = Pos.Center (),
+    Y = Pos.Center ()
+};
+window.Add (label);
+
+app.Run (window);
+```
+
+See the [Examples](Examples/) directory for more.
+
+# Build Powerful Terminal Applications
+
+Terminal.Gui enables building sophisticated console applications with modern UIs:
+
+- **Rich Forms and Dialogs** - Text fields, buttons, checkboxes, radio buttons, and data validation
+- **Interactive Data Views** - Tables, lists, and trees with sorting, filtering, and in-place editing  
+- **Visualizations** - Charts, graphs, progress indicators, and color pickers with TrueColor support
+- **Text Editors** - Full-featured text editing with clipboard, undo/redo, and Unicode support
+- **File Management** - File and directory browsers with search and filtering
+- **Wizards and Multi-Step Processes** - Guided workflows with navigation and validation
+- **System Monitoring Tools** - Real-time dashboards with scrollable, resizable views
+- **Configuration UIs** - Settings editors with persistent themes and user preferences
+- **Cross-Platform CLI Tools** - Consistent experience on Windows, macOS, and Linux
+- **Server Management Interfaces** - SSH-compatible UIs for remote administration
+
+See the [Views Overview](https://gui-cs.github.io/Terminal.Gui/docs/views) for available controls and [What's New in v2](https://gui-cs.github.io/Terminal.Gui/docs/newinv2) for architectural improvements.
 
 
 # Documentation 
 # Documentation 
 
 
-The full developer documentation for Terminal.Gui is available at [gui-cs.github.io/Terminal.Gui](https://gui-cs.github.io/Terminal.Gui).
+Comprehensive documentation is at [gui-cs.github.io/Terminal.Gui](https://gui-cs.github.io/Terminal.Gui).
 
 
 ## Getting Started
 ## Getting Started
 
 
-- [Getting Started](https://gui-cs.github.io/Terminal.Gui/docs/getting-started) - Quick start guide to create your first Terminal.Gui application
-- [Migrating from v1 to v2](https://gui-cs.github.io/Terminal.Gui/docs/migratingfromv1) - Complete guide for upgrading existing applications
-- [What's New in v2](https://gui-cs.github.io/Terminal.Gui/docs/newinv2) - Overview of new features and improvements
+- **[Getting Started Guide](https://gui-cs.github.io/Terminal.Gui/docs/getting-started)** - First Terminal.Gui application
+- **[API Reference](https://gui-cs.github.io/Terminal.Gui/api/Terminal.Gui.App.html)** - Complete API documentation
+- **[What's New in v2](https://gui-cs.github.io/Terminal.Gui/docs/newinv2)** - New features and improvements
 
 
-## API Reference
+## Migration & Deep Dives
 
 
-For detailed API documentation, see the [API Reference](https://gui-cs.github.io/Terminal.Gui/api/Terminal.Gui.App.html).
+- **[Migrating from v1 to v2](https://gui-cs.github.io/Terminal.Gui/docs/migratingfromv1)** - Complete migration guide
+- **[Application Architecture](https://gui-cs.github.io/Terminal.Gui/docs/application)** - Instance-based model and IRunnable pattern
+- **[Layout System](https://gui-cs.github.io/Terminal.Gui/docs/layout)** - Positioning, sizing, and adornments
+- **[Keyboard Handling](https://gui-cs.github.io/Terminal.Gui/docs/keyboard)** - Key bindings and commands
+- **[View Documentation](https://gui-cs.github.io/Terminal.Gui/docs/View)** - View hierarchy and lifecycle
+- **[Configuration](https://gui-cs.github.io/Terminal.Gui/docs/config)** - Themes and persistent settings
 
 
-# Installing
+See the [documentation index](https://gui-cs.github.io/Terminal.Gui/docs/index) for all topics.
 
 
-Use NuGet to install the `Terminal.Gui` NuGet package: 
+# Installing
 
 
-## v2 Alpha 
+## v2 Alpha (Recommended)
 
 
-(Infrequently updated, but stable enough for production use)
-```
+```powershell
 dotnet add package Terminal.Gui --version "2.0.0-alpha.*"
 dotnet add package Terminal.Gui --version "2.0.0-alpha.*"
 ```
 ```
 
 
-## v2 Develop
+## v2 Develop (Latest)
 
 
-(Frequently updated, but may have breaking changes)
-```
+```powershell
 dotnet add package Terminal.Gui --version "2.0.0-develop.*"
 dotnet add package Terminal.Gui --version "2.0.0-develop.*"
 ```
 ```
 
 
-## Legacy v1
+## v1 Legacy
 
 
-```
-dotnet add package Terminal.Gui --version "1.*
+```powershell
+dotnet add package Terminal.Gui --version "1.*"
 ```
 ```
 
 
-Or, you can use the [Terminal.Gui.Templates](https://github.com/gui-cs/Terminal.Gui.templates).
+Or use the [Terminal.Gui.Templates](https://github.com/gui-cs/Terminal.Gui.templates).
 
 
 # Contributing
 # Contributing
 
 
-See [CONTRIBUTING.md](CONTRIBUTING.md) for complete contribution guidelines.
-
-Debates on architecture and design can be found in Issues tagged with [design](https://github.com/gui-cs/Terminal.Gui/issues?q=is%3Aopen+is%3Aissue+label%3Av2+label%3Adesign).
+Contributions welcome! See [CONTRIBUTING.md](CONTRIBUTING.md).
 
 
 # History
 # History
 
 
-See [gui-cs](https://github.com/gui-cs/) for how this project came to be.
+See [gui-cs](https://github.com/gui-cs/) for project history and origins.

+ 9 - 25
Terminal.Gui/App/Application.Driver.cs

@@ -1,4 +1,6 @@
 
 
+
+using System.Collections.Concurrent;
 using System.Diagnostics.CodeAnalysis;
 using System.Diagnostics.CodeAnalysis;
 
 
 namespace Terminal.Gui.App;
 namespace Terminal.Gui.App;
@@ -13,30 +15,13 @@ public static partial class Application // Driver abstractions
         internal set => ApplicationImpl.Instance.Driver = value;
         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 => _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;
-
+    // NOTE: ForceDriver is a configuration property (Application.ForceDriver).
+    // NOTE: IApplication also has a ForceDriver property, which is an instance property
+    // NOTE: set whenever this static property is set.
     private static string _forceDriver = string.Empty; // Resources/config.json overrides
     private static string _forceDriver = string.Empty; // Resources/config.json overrides
 
 
     /// <inheritdoc cref="IApplication.ForceDriver"/>
     /// <inheritdoc cref="IApplication.ForceDriver"/>
     [ConfigurationProperty (Scope = typeof (SettingsScope))]
     [ConfigurationProperty (Scope = typeof (SettingsScope))]
-    [Obsolete ("The legacy static Application object is going away.")]
     public static string ForceDriver
     public static string ForceDriver
     {
     {
         get => _forceDriver;
         get => _forceDriver;
@@ -44,16 +29,15 @@ public static partial class Application // Driver abstractions
         {
         {
             string oldValue = _forceDriver;
             string oldValue = _forceDriver;
             _forceDriver = value;
             _forceDriver = value;
-            ForceDriverChanged?.Invoke (null, new ValueChangedEventArgs<string> (oldValue, _forceDriver));
+            ForceDriverChanged?.Invoke (null, new (oldValue, _forceDriver));
         }
         }
     }
     }
 
 
     /// <summary>Raised when <see cref="ForceDriver"/> changes.</summary>
     /// <summary>Raised when <see cref="ForceDriver"/> changes.</summary>
     public static event EventHandler<ValueChangedEventArgs<string>>? ForceDriverChanged;
     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;
+    /// <inheritdoc cref="IDriver.GetSixels"/>
+    public static ConcurrentQueue<SixelToRender> GetSixels () => ApplicationImpl.Instance.Driver?.GetSixels ()!;
 
 
     /// <summary>Gets a list of <see cref="IDriver"/> types and type names that are available.</summary>
     /// <summary>Gets a list of <see cref="IDriver"/> types and type names that are available.</summary>
     /// <returns></returns>
     /// <returns></returns>
@@ -67,7 +51,7 @@ public static partial class Application // Driver abstractions
         // Only inspect the IDriver assembly
         // Only inspect the IDriver assembly
         var asm = typeof (IDriver).Assembly;
         var asm = typeof (IDriver).Assembly;
 
 
-        foreach (Type? type in asm.GetTypes ())
+        foreach (Type type in asm.GetTypes ())
         {
         {
             if (typeof (IDriver).IsAssignableFrom (type) && type is { IsAbstract: false, IsClass: true })
             if (typeof (IDriver).IsAssignableFrom (type) && type is { IsAbstract: false, IsClass: true })
             {
             {

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

@@ -10,6 +10,22 @@ namespace Terminal.Gui.App;
 
 
 public static partial class Application // Lifecycle (Init/Shutdown)
 public static partial class Application // Lifecycle (Init/Shutdown)
 {
 {
+    /// <summary>
+    ///     Gets the singleton <see cref="IApplication"/> instance used by the legacy static Application model.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         For new code, prefer using <see cref="Create"/> to get an instance-based application.
+    ///         This property is provided for backward compatibility and internal use.
+    ///     </para>
+    ///     <para>
+    ///         This property returns the same singleton instance used by the legacy static <see cref="Application"/>
+    ///         methods like <see cref="Init"/> and <see cref="Run(IRunnable, Func{Exception, bool}?)"/>.
+    ///     </para>
+    /// </remarks>
+    [Obsolete ("The legacy static Application object is going away. Use Application.Create() for new code.")]
+    public static IApplication Instance => ApplicationImpl.Instance;
+
     /// <summary>
     /// <summary>
     ///     Creates a new <see cref="IApplication"/> instance.
     ///     Creates a new <see cref="IApplication"/> instance.
     /// </summary>
     /// </summary>
@@ -49,7 +65,7 @@ public static partial class Application // Lifecycle (Init/Shutdown)
         internal set => ApplicationImpl.Instance.MainThreadId = value;
         internal set => ApplicationImpl.Instance.MainThreadId = value;
     }
     }
 
 
-    /// <inheritdoc cref="IApplication.Dispose"/>
+    /// <inheritdoc cref="IDisposable.Dispose"/>
     [Obsolete ("The legacy static Application object is going away.")]
     [Obsolete ("The legacy static Application object is going away.")]
     public static void Shutdown () => ApplicationImpl.Instance.Dispose ();
     public static void Shutdown () => ApplicationImpl.Instance.Dispose ();
 
 

+ 0 - 117
Terminal.Gui/App/Application.cd

@@ -1,117 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<ClassDiagram MajorVersion="1" MinorVersion="1">
-  <Class Name="Terminal.Gui.App.Application">
-    <Position X="2.25" Y="1.5" Width="1.5" />
-    <TypeIdentifier>
-      <HashCode>gEK4FIgQOAQIuhQeBwoUgSCgAAJL0AACESIKoAiBWw8=</HashCode>
-      <FileName>App\Application.cs</FileName>
-    </TypeIdentifier>
-  </Class>
-  <Class Name="Terminal.Gui.App.ApplicationNavigation" Collapsed="true">
-    <Position X="14.75" Y="2.75" Width="2" />
-    <TypeIdentifier>
-      <HashCode>AABAAAAAAABCAAAAAAAAAAAAAAAAIgIAAAAAAAAAAAA=</HashCode>
-      <FileName>App\ApplicationNavigation.cs</FileName>
-    </TypeIdentifier>
-  </Class>
-  <Class Name="Terminal.Gui.App.IterationEventArgs" Collapsed="true">
-    <Position X="17" Y="3" Width="2" />
-    <TypeIdentifier>
-      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
-      <FileName>App\IterationEventArgs.cs</FileName>
-    </TypeIdentifier>
-  </Class>
-  <Class Name="Terminal.Gui.App.MainLoop" Collapsed="true" BaseTypeListCollapsed="true">
-    <Position X="11.25" Y="3.75" Width="1.5" />
-    <TypeIdentifier>
-      <HashCode>AAAAAAAAACAAAAAAAAAAAAAACBAAEAAIIAIAgAAAEAI=</HashCode>
-      <FileName>App\MainLoop.cs</FileName>
-    </TypeIdentifier>
-    <Lollipop Position="0.2" Collapsed="true" />
-  </Class>
-  <Class Name="Terminal.Gui.App.MainLoopSyncContext" Collapsed="true">
-    <Position X="13" Y="3.75" Width="2" />
-    <TypeIdentifier>
-      <HashCode>AAAAAgAAAAAAAAAAAEAAAAAACAAAAAAAAAAAAAAAAAA=</HashCode>
-      <FileName>App\MainLoopSyncContext.cs</FileName>
-    </TypeIdentifier>
-  </Class>
-  <Class Name="Terminal.Gui.App.SessionToken" Collapsed="true" BaseTypeListCollapsed="true">
-    <Position X="15.25" Y="4" Width="1.5" />
-    <TypeIdentifier>
-      <HashCode>AAAAAAAAACACAgAAAAAAAAAAAAAAAAACQAAAAAAAAAA=</HashCode>
-      <FileName>App\SessionToken.cs</FileName>
-    </TypeIdentifier>
-    <Lollipop Position="0.2" Collapsed="true" />
-  </Class>
-  <Class Name="Terminal.Gui.App.SessionTokenEventArgs" Collapsed="true">
-    <Position X="17" Y="4" Width="2" />
-    <TypeIdentifier>
-      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA=</HashCode>
-      <FileName>App\SessionTokenEventArgs.cs</FileName>
-    </TypeIdentifier>
-  </Class>
-  <Class Name="Terminal.Gui.App.Timeout" Collapsed="true">
-    <Position X="11.25" Y="4.75" Width="1.5" />
-    <TypeIdentifier>
-      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAQAA=</HashCode>
-      <FileName>App\Timeout.cs</FileName>
-    </TypeIdentifier>
-  </Class>
-  <Class Name="Terminal.Gui.App.TimeoutEventArgs" Collapsed="true">
-    <Position X="13" Y="4.75" Width="2" />
-    <TypeIdentifier>
-      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAACAIAAAAAAAAAAAA=</HashCode>
-      <FileName>App\TimeoutEventArgs.cs</FileName>
-    </TypeIdentifier>
-  </Class>
-  <Class Name="Terminal.Gui.App.ApplicationImpl" BaseTypeListCollapsed="true">
-    <Position X="4" Y="5" Width="2" />
-    <TypeIdentifier>
-      <HashCode>AABgAAAAIAAIAgQUAAAAAQAAAAAAAAAAQAAKgAAAEAI=</HashCode>
-      <FileName>App\ApplicationImpl.cs</FileName>
-    </TypeIdentifier>
-    <Lollipop Position="0.2" />
-  </Class>
-  <Class Name="Terminal.Gui.App.MouseGrabHandler" Collapsed="true">
-    <Position X="6.25" Y="9.25" Width="2" />
-    <TypeIdentifier>
-      <HashCode>BAAgAAAAgABAAoAAAAAAABAAACEAAAAAAABAAgAAAAA=</HashCode>
-      <FileName>App\MouseGrabHandler.cs</FileName>
-    </TypeIdentifier>
-    <Lollipop Position="0.2" />
-  </Class>
-  <Interface Name="Terminal.Gui.App.IMainLoopDriver">
-    <Position X="11.25" Y="1.5" Width="1.5" />
-    <TypeIdentifier>
-      <HashCode>AAAAAAAACAAAAAQAAAAABAAAAAAAEAAAAAAAAAAAAAA=</HashCode>
-      <FileName>App\MainLoop.cs</FileName>
-    </TypeIdentifier>
-  </Interface>
-  <Interface Name="Terminal.Gui.App.IApplication">
-    <Position X="4" Y="1.5" Width="1.5" />
-    <TypeIdentifier>
-      <HashCode>AAAgAAAAAAAIAgQUAAAAAQAAAAAAAAAAAAAKgAAAEAI=</HashCode>
-      <FileName>App\IApplication.cs</FileName>
-    </TypeIdentifier>
-    <ShowAsAssociation>
-      <Property Name="MouseGrabHandler" />
-      <Property Name="TimedEvents" />
-    </ShowAsAssociation>
-  </Interface>
-  <Interface Name="Terminal.Gui.App.IMouseGrabHandler">
-    <Position X="7" Y="1.5" Width="2" />
-    <TypeIdentifier>
-      <HashCode>BAAgAAAAAAAAAgAAAAAAABAAACEAAAAAAAAAAgAAAAA=</HashCode>
-      <FileName>App\IMouseGrabHandler.cs</FileName>
-    </TypeIdentifier>
-  </Interface>
-  <Interface Name="Terminal.Gui.App.ITimedEvents">
-    <Position X="7" Y="4.5" Width="2" />
-    <TypeIdentifier>
-      <HashCode>BAAAIAAAAQAAAAAQACAAAIBAAQAAAAAAAAAIgAAAAAA=</HashCode>
-      <FileName>App\ITimedEvents.cs</FileName>
-    </TypeIdentifier>
-  </Interface>
-  <Font Name="Segoe UI" Size="9" />
-</ClassDiagram>

+ 13 - 9
Terminal.Gui/App/ApplicationImpl.Driver.cs

@@ -2,20 +2,14 @@ using System.Collections.Concurrent;
 
 
 namespace Terminal.Gui.App;
 namespace Terminal.Gui.App;
 
 
-public partial class ApplicationImpl
+internal partial class ApplicationImpl
 {
 {
     /// <inheritdoc/>
     /// <inheritdoc/>
     public IDriver? Driver { get; set; }
     public IDriver? Driver { get; set; }
 
 
-    /// <inheritdoc/>
-    public bool Force16Colors { get; set; }
-
     /// <inheritdoc/>
     /// <inheritdoc/>
     public string ForceDriver { get; set; } = string.Empty;
     public string ForceDriver { get; set; } = string.Empty;
 
 
-    /// <inheritdoc/>
-    public List<SixelToRender> Sixel { get; } = new ();
-
     /// <summary>
     /// <summary>
     ///     Creates the appropriate <see cref="IDriver"/> based on platform and driverName.
     ///     Creates the appropriate <see cref="IDriver"/> based on platform and driverName.
     /// </summary>
     /// </summary>
@@ -85,6 +79,8 @@ public partial class ApplicationImpl
         {
         {
             throw new ("Driver was null even after booting MainLoopCoordinator");
             throw new ("Driver was null even after booting MainLoopCoordinator");
         }
         }
+
+        Driver.Force16Colors = Terminal.Gui.Drivers.Driver.Force16Colors;
     }
     }
 
 
     private readonly IComponentFactory? _componentFactory;
     private readonly IComponentFactory? _componentFactory;
@@ -149,7 +145,11 @@ public partial class ApplicationImpl
 
 
     internal void SubscribeDriverEvents ()
     internal void SubscribeDriverEvents ()
     {
     {
-        ArgumentNullException.ThrowIfNull (Driver);
+        if (Driver is null)
+        {
+            Logging.Error($"Driver is null");
+            return;
+        }
 
 
         Driver.SizeChanged += Driver_SizeChanged;
         Driver.SizeChanged += Driver_SizeChanged;
         Driver.KeyDown += Driver_KeyDown;
         Driver.KeyDown += Driver_KeyDown;
@@ -159,7 +159,11 @@ public partial class ApplicationImpl
 
 
     internal void UnsubscribeDriverEvents ()
     internal void UnsubscribeDriverEvents ()
     {
     {
-        ArgumentNullException.ThrowIfNull (Driver);
+        if (Driver is null)
+        {
+            Logging.Error ($"Driver is null");
+            return;
+        }
 
 
         Driver.SizeChanged -= Driver_SizeChanged;
         Driver.SizeChanged -= Driver_SizeChanged;
         Driver.KeyDown -= Driver_KeyDown;
         Driver.KeyDown -= Driver_KeyDown;

+ 5 - 25
Terminal.Gui/App/ApplicationImpl.Lifecycle.cs

@@ -3,7 +3,7 @@ using System.Diagnostics.CodeAnalysis;
 
 
 namespace Terminal.Gui.App;
 namespace Terminal.Gui.App;
 
 
-public partial class ApplicationImpl
+internal partial class ApplicationImpl
 {
 {
     /// <inheritdoc/>
     /// <inheritdoc/>
     public int? MainThreadId { get; set; }
     public int? MainThreadId { get; set; }
@@ -87,7 +87,7 @@ public partial class ApplicationImpl
         _keyboard.PrevTabGroupKey = existingPrevTabGroupKey;
         _keyboard.PrevTabGroupKey = existingPrevTabGroupKey;
 
 
         CreateDriver (_driverName);
         CreateDriver (_driverName);
-        Screen = Driver!.Screen;
+
         Initialized = true;
         Initialized = true;
 
 
         RaiseInitializedChanged (this, new (true));
         RaiseInitializedChanged (this, new (true));
@@ -269,14 +269,10 @@ public partial class ApplicationImpl
         if (Driver is { })
         if (Driver is { })
         {
         {
             UnsubscribeDriverEvents ();
             UnsubscribeDriverEvents ();
-            Driver?.End ();
+            Driver.Dispose ();
             Driver = null;
             Driver = null;
         }
         }
 
 
-        // Reset screen
-        ResetScreen ();
-        _screen = null;
-
         // === 5. Clear run state ===
         // === 5. Clear run state ===
         Iteration = null;
         Iteration = null;
         SessionBegun = null;
         SessionBegun = null;
@@ -304,23 +300,11 @@ public partial class ApplicationImpl
         // === 7. Clear navigation and screen state ===
         // === 7. Clear navigation and screen state ===
         ScreenChanged = null;
         ScreenChanged = null;
 
 
-        //Navigation = null;
-
         // === 8. Reset initialization state ===
         // === 8. Reset initialization state ===
         Initialized = false;
         Initialized = false;
         MainThreadId = null;
         MainThreadId = null;
 
 
-        // === 9. Clear graphics ===
-        Sixel.Clear ();
-
-        // === 10. Reset ForceDriver ===
-        // Note: ForceDriver and Force16Colors are reset
-        // If they need to persist across Init/Shutdown cycles
-        // then the user of the library should manage that state
-        Force16Colors = false;
-        ForceDriver = string.Empty;
-
-        // === 11. Reset synchronization context ===
+        // === 9. Reset synchronization context ===
         // IMPORTANT: Always reset sync context, even if not initialized
         // IMPORTANT: Always reset sync context, even if not initialized
         // This ensures cleanup works correctly even if Shutdown is called without Init
         // This ensures cleanup works correctly even if Shutdown is called without Init
         // Reset synchronization context to allow the user to run async/await,
         // Reset synchronization context to allow the user to run async/await,
@@ -329,7 +313,7 @@ public partial class ApplicationImpl
         // (https://github.com/gui-cs/Terminal.Gui/issues/1084).
         // (https://github.com/gui-cs/Terminal.Gui/issues/1084).
         SynchronizationContext.SetSynchronizationContext (null);
         SynchronizationContext.SetSynchronizationContext (null);
 
 
-        // === 12. Unsubscribe from Application static property change events ===
+        // === 10. Unsubscribe from Application static property change events ===
         UnsubscribeApplicationEvents ();
         UnsubscribeApplicationEvents ();
     }
     }
 
 
@@ -368,9 +352,6 @@ public partial class ApplicationImpl
     }
     }
 #endif
 #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; }
     private void OnForceDriverChanged (object? sender, ValueChangedEventArgs<string> e) { ForceDriver = e.NewValue; }
 
 
     /// <summary>
     /// <summary>
@@ -378,7 +359,6 @@ public partial class ApplicationImpl
     /// </summary>
     /// </summary>
     private void UnsubscribeApplicationEvents ()
     private void UnsubscribeApplicationEvents ()
     {
     {
-        Application.Force16ColorsChanged -= OnForce16ColorsChanged;
         Application.ForceDriverChanged -= OnForceDriverChanged;
         Application.ForceDriverChanged -= OnForceDriverChanged;
     }
     }
 }
 }

+ 1 - 1
Terminal.Gui/App/ApplicationImpl.Run.cs

@@ -3,7 +3,7 @@ using System.Diagnostics.CodeAnalysis;
 
 
 namespace Terminal.Gui.App;
 namespace Terminal.Gui.App;
 
 
-public partial class ApplicationImpl
+internal partial class ApplicationImpl
 {
 {
     // Lock object to protect session stack operations and cached state updates
     // Lock object to protect session stack operations and cached state updates
     private readonly object _sessionStackLock = new ();
     private readonly object _sessionStackLock = new ();

+ 28 - 45
Terminal.Gui/App/ApplicationImpl.Screen.cs

@@ -1,29 +1,14 @@
-
 namespace Terminal.Gui.App;
 namespace Terminal.Gui.App;
 
 
-public partial class ApplicationImpl
+internal partial class ApplicationImpl
 {
 {
     /// <inheritdoc/>
     /// <inheritdoc/>
     public event EventHandler<EventArgs<Rectangle>>? ScreenChanged;
     public event EventHandler<EventArgs<Rectangle>>? ScreenChanged;
 
 
-    private readonly object _lockScreen = new ();
-    private Rectangle? _screen;
-
     /// <inheritdoc/>
     /// <inheritdoc/>
     public Rectangle Screen
     public Rectangle Screen
     {
     {
-        get
-        {
-            lock (_lockScreen)
-            {
-                if (_screen == null)
-                {
-                    _screen = Driver?.Screen ?? new (new (0, 0), new (2048, 2048));
-                }
-
-                return _screen.Value;
-            }
-        }
+        get => Driver?.Screen ?? new (new (0, 0), new (2048, 2048));
         set
         set
         {
         {
             if (value is { } && (value.X != 0 || value.Y != 0))
             if (value is { } && (value.X != 0 || value.Y != 0))
@@ -31,10 +16,7 @@ public partial class ApplicationImpl
                 throw new NotImplementedException ("Screen locations other than 0, 0 are not yet supported");
                 throw new NotImplementedException ("Screen locations other than 0, 0 are not yet supported");
             }
             }
 
 
-            lock (_lockScreen)
-            {
-                _screen = value;
-            }
+            Driver?.SetScreenSize (value.Width, value.Height);
         }
         }
     }
     }
 
 
@@ -114,16 +96,6 @@ public partial class ApplicationImpl
         return false;
         return false;
     }
     }
 
 
-    /// <summary>
-    ///     INTERNAL: Resets the Screen field to null so it will be recalculated on next access.
-    /// </summary>
-    private void ResetScreen ()
-    {
-        lock (_lockScreen)
-        {
-            _screen = null;
-        }
-    }
 
 
     /// <summary>
     /// <summary>
     ///     INTERNAL: Called when the application's screen has changed.
     ///     INTERNAL: Called when the application's screen has changed.
@@ -132,7 +104,7 @@ public partial class ApplicationImpl
     /// <param name="screen">The new screen size and position.</param>
     /// <param name="screen">The new screen size and position.</param>
     private void RaiseScreenChangedEvent (Rectangle screen)
     private void RaiseScreenChangedEvent (Rectangle screen)
     {
     {
-        Screen = new (Point.Empty, screen.Size);
+        //Screen = new (Point.Empty, screen.Size);
 
 
         ScreenChanged?.Invoke (this, new (screen));
         ScreenChanged?.Invoke (this, new (screen));
 
 
@@ -150,17 +122,6 @@ public partial class ApplicationImpl
     /// <inheritdoc/>
     /// <inheritdoc/>
     public void LayoutAndDraw (bool forceRedraw = false)
     public void LayoutAndDraw (bool forceRedraw = false)
     {
     {
-        List<View?> tops = [.. SessionStack!.Select(r => r.Runnable! as View)!];
-
-        if (Popover?.GetActivePopover () as View is { Visible: true } visiblePopover)
-        {
-            visiblePopover.SetNeedsDraw ();
-            visiblePopover.SetNeedsLayout ();
-            tops.Insert (0, visiblePopover);
-        }
-
-        bool neededLayout = View.Layout (tops.ToArray ().Reverse ()!, Screen.Size);
-
         if (ClearScreenNextIteration)
         if (ClearScreenNextIteration)
         {
         {
             forceRedraw = true;
             forceRedraw = true;
@@ -172,12 +133,34 @@ public partial class ApplicationImpl
             Driver?.ClearContents ();
             Driver?.ClearContents ();
         }
         }
 
 
-        if (Driver is { })
+        List<View?> views = [.. SessionStack!.Select (r => r.Runnable! as View)!];
+
+        if (Popover?.GetActivePopover () as View is { Visible: true } visiblePopover)
+        {
+            visiblePopover.SetNeedsDraw ();
+            visiblePopover.SetNeedsLayout ();
+            views.Insert (0, visiblePopover);
+        }
+
+        // Layout
+        bool neededLayout = View.Layout (views.ToArray ().Reverse ()!, Screen.Size);
+
+        // Draw
+        bool needsDraw = forceRedraw || views.Any (v => v is { NeedsDraw: true } or { SubViewNeedsDraw: true });
+
+        if (Driver is { } && (neededLayout || needsDraw))
         {
         {
+            Logging.Redraws.Add (1);
+
             Driver.Clip = new (Screen);
             Driver.Clip = new (Screen);
 
 
-            View.Draw (views: tops!, neededLayout || forceRedraw);
+            // Only force a complete redraw if needed (needsLayout or forceRedraw).
+            // Otherwise, just redraw views that need it.
+            View.Draw (views: views.ToArray ().Cast<View> (), neededLayout || forceRedraw);
+
             Driver.Clip = new (Screen);
             Driver.Clip = new (Screen);
+
+            // Cause the driver to flush any pending updates to the terminal
             Driver?.Refresh ();
             Driver?.Refresh ();
         }
         }
     }
     }

+ 2 - 15
Terminal.Gui/App/ApplicationImpl.cs

@@ -6,7 +6,7 @@ namespace Terminal.Gui.App;
 ///     Implementation of core <see cref="Application"/> methods using the modern
 ///     Implementation of core <see cref="Application"/> methods using the modern
 ///     main loop architecture with component factories for different platforms.
 ///     main loop architecture with component factories for different platforms.
 /// </summary>
 /// </summary>
-public partial class ApplicationImpl : IApplication
+internal partial class ApplicationImpl : IApplication
 {
 {
     /// <summary>
     /// <summary>
     ///     INTERNAL: Creates a new instance of the Application backend and subscribes to Application configuration property
     ///     INTERNAL: Creates a new instance of the Application backend and subscribes to Application configuration property
@@ -15,7 +15,6 @@ public partial class ApplicationImpl : IApplication
     internal ApplicationImpl ()
     internal ApplicationImpl ()
     {
     {
         // Subscribe to Application static property change events
         // Subscribe to Application static property change events
-        Application.Force16ColorsChanged += OnForce16ColorsChanged;
         Application.ForceDriverChanged += OnForceDriverChanged;
         Application.ForceDriverChanged += OnForceDriverChanged;
     }
     }
 
 
@@ -75,7 +74,7 @@ public partial class ApplicationImpl : IApplication
     /// <summary>
     /// <summary>
     ///     Gets the currently configured backend implementation of <see cref="Application"/> gateway methods.
     ///     Gets the currently configured backend implementation of <see cref="Application"/> gateway methods.
     /// </summary>
     /// </summary>
-    public static IApplication Instance
+    internal static IApplication Instance
     {
     {
         get
         get
         {
         {
@@ -143,18 +142,6 @@ public partial class ApplicationImpl : IApplication
         // If an instance exists, reset it
         // If an instance exists, reset it
         _instance?.ResetState (ignoreDisposed);
         _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
         // Always reset the model tracking to allow tests to use either model after reset
         ResetModelUsageTracking ();
         ResetModelUsageTracking ();
     }
     }

+ 0 - 13
Terminal.Gui/App/IApplication.cs

@@ -449,13 +449,6 @@ public interface IApplication : IDisposable
     /// </remarks>
     /// </remarks>
     IClipboard? Clipboard { get; }
     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
-    ///     output as long as the selected <see cref="IDriver"/> supports TrueColor.
-    /// </summary>
-    bool Force16Colors { get; set; }
-
     /// <summary>
     /// <summary>
     ///     Forces the use of the specified driver (one of "fake", "dotnet", "windows", or "unix"). If not
     ///     Forces the use of the specified driver (one of "fake", "dotnet", "windows", or "unix"). If not
     ///     specified, the driver is selected based on the platform.
     ///     specified, the driver is selected based on the platform.
@@ -497,12 +490,6 @@ public interface IApplication : IDisposable
     /// </remarks>
     /// </remarks>
     bool ClearScreenNextIteration { get; set; }
     bool ClearScreenNextIteration { get; set; }
 
 
-    /// <summary>
-    ///     Collection of sixel images to write out to screen when updating.
-    ///     Only add to this collection if you are sure terminal supports sixel format.
-    /// </summary>
-    List<SixelToRender> Sixel { get; }
-
     #endregion Screen and Driver
     #endregion Screen and Driver
 
 
     #region Keyboard
     #region Keyboard

+ 12 - 48
Terminal.Gui/App/MainLoop/ApplicationMainLoop.cs

@@ -137,30 +137,18 @@ public class ApplicationMainLoop<TInputRecord> : IApplicationMainLoop<TInputReco
         // Pull any input events from the input queue and process them
         // Pull any input events from the input queue and process them
         InputProcessor.ProcessQueue ();
         InputProcessor.ProcessQueue ();
 
 
-        if (App?.TopRunnableView != null)
-        {
-            bool needsDrawOrLayout = AnySubViewsNeedDrawn (App?.Popover?.GetActivePopover () as View)
-                                     || AnySubViewsNeedDrawn (App?.TopRunnableView)
-                                     || (App?.Mouse.MouseGrabView != null && AnySubViewsNeedDrawn (App?.Mouse.MouseGrabView));
-
-            bool sizeChanged = SizeMonitor.Poll ();
-
-            if (needsDrawOrLayout || sizeChanged)
-            {
-                Logging.Redraws.Add (1);
-
-                App?.LayoutAndDraw (true);
-
-                Output.Write (OutputBuffer);
+        // Check for any size changes; this will cause SizeChanged events
+        SizeMonitor.Poll ();
 
 
-                Output.SetCursorVisibility (CursorVisibility.Default);
-            }
+        // Layout and draw any views that need it
+        App?.LayoutAndDraw (forceRedraw: false);
 
 
-            SetCursor ();
-        }
+        // Update the cursor
+        SetCursor ();
 
 
-        var swCallbacks = Stopwatch.StartNew ();
+        Stopwatch swCallbacks = Stopwatch.StartNew ();
 
 
+        // Run any timeout callbacks that are due
         TimedEvents.RunTimers ();
         TimedEvents.RunTimers ();
 
 
         Logging.IterationInvokesAndTimeouts.Record (swCallbacks.Elapsed.Milliseconds);
         Logging.IterationInvokesAndTimeouts.Record (swCallbacks.Elapsed.Milliseconds);
@@ -168,10 +156,11 @@ public class ApplicationMainLoop<TInputRecord> : IApplicationMainLoop<TInputReco
 
 
     private void SetCursor ()
     private void SetCursor ()
     {
     {
-        View? mostFocused = App?.TopRunnableView!.MostFocused;
+        View? mostFocused = App?.TopRunnableView?.MostFocused;
 
 
         if (mostFocused == null)
         if (mostFocused == null)
         {
         {
+            Output.SetCursorVisibility (CursorVisibility.Invisible);
             return;
             return;
         }
         }
 
 
@@ -180,9 +169,9 @@ public class ApplicationMainLoop<TInputRecord> : IApplicationMainLoop<TInputReco
         if (to.HasValue)
         if (to.HasValue)
         {
         {
             // Translate to screen coordinates
             // Translate to screen coordinates
-            to = mostFocused.ViewportToScreen (to.Value);
+            Point screenPos = mostFocused.ViewportToScreen (to.Value);
 
 
-            Output.SetCursorPosition (to.Value.X, to.Value.Y);
+            Output.SetCursorPosition (screenPos.X, screenPos.Y);
             Output.SetCursorVisibility (mostFocused.CursorVisibility);
             Output.SetCursorVisibility (mostFocused.CursorVisibility);
         }
         }
         else
         else
@@ -191,31 +180,6 @@ public class ApplicationMainLoop<TInputRecord> : IApplicationMainLoop<TInputReco
         }
         }
     }
     }
 
 
-    private bool AnySubViewsNeedDrawn (View? v)
-    {
-        if (v is null)
-        {
-            return false;
-        }
-
-        if (v.NeedsDraw || v.NeedsLayout)
-        {
-            // Logging.Trace ($"{v.GetType ().Name} triggered redraw (NeedsDraw={v.NeedsDraw} NeedsLayout={v.NeedsLayout}) ");
-
-            return true;
-        }
-
-        foreach (View subview in v.SubViews)
-        {
-            if (AnySubViewsNeedDrawn (subview))
-            {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
     /// <inheritdoc/>
     /// <inheritdoc/>
     public void Dispose ()
     public void Dispose ()
     {
     {

+ 2 - 8
Terminal.Gui/App/Mouse/MouseImpl.cs

@@ -20,14 +20,8 @@ internal class MouseImpl : IMouse, IDisposable
         Application.IsMouseDisabledChanged += OnIsMouseDisabledChanged;
         Application.IsMouseDisabledChanged += OnIsMouseDisabledChanged;
     }
     }
 
 
-    private IApplication? _app;
-
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public IApplication? App
-    {
-        get => _app;
-        set => _app = value;
-    }
+    public IApplication? App { get; set; }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
     public Point? LastMousePosition { get; set; }
     public Point? LastMousePosition { get; set; }
@@ -248,7 +242,7 @@ internal class MouseImpl : IMouse, IDisposable
                 continue;
                 continue;
             }
             }
 
 
-            CancelEventArgs eventArgs = new System.ComponentModel.CancelEventArgs ();
+            CancelEventArgs eventArgs = new CancelEventArgs ();
             bool? cancelled = view.NewMouseEnterEvent (eventArgs);
             bool? cancelled = view.NewMouseEnterEvent (eventArgs);
 
 
             if (cancelled is true || eventArgs.Cancel)
             if (cancelled is true || eventArgs.Cancel)

+ 32 - 17
Terminal.Gui/App/Timeout/TimedEvents.cs

@@ -201,32 +201,47 @@ public class TimedEvents : ITimedEvents
     private void RunTimersImpl ()
     private void RunTimersImpl ()
     {
     {
         long now = GetTimestampTicks ();
         long now = GetTimestampTicks ();
-        SortedList<long, Timeout> copy;
 
 
-        // lock prevents new timeouts being added
-        // after we have taken the copy but before
-        // we have allocated a new list (which would
-        // result in lost timeouts or errors during enumeration)
-        lock (_timeoutsLockToken)
+        // Process due timeouts one at a time, without blocking the entire queue
+        while (true)
         {
         {
-            copy = _timeouts;
-            _timeouts = new ();
-        }
+            Timeout? timeoutToExecute = null;
+            long scheduledTime = 0;
 
 
-        foreach ((long k, Timeout timeout) in copy)
-        {
-            if (k < now)
+            // Find the next due timeout
+            lock (_timeoutsLockToken)
             {
             {
-                if (timeout.Callback! ())
+                if (_timeouts.Count == 0)
+                {
+                    break; // No more timeouts
+                }
+
+                // Re-evaluate current time for each iteration
+                now = GetTimestampTicks ();
+                
+                // Check if the earliest timeout is due
+                scheduledTime = _timeouts.Keys [0];
+                
+                if (scheduledTime >= now)
                 {
                 {
-                    AddTimeout (timeout.Span, timeout);
+                    // Earliest timeout is not yet due, we're done
+                    break;
                 }
                 }
+
+                // This timeout is due - remove it from the queue
+                timeoutToExecute = _timeouts.Values [0];
+                _timeouts.RemoveAt (0);
             }
             }
-            else
+
+            // Execute the callback outside the lock
+            // This allows nested Run() calls to access the timeout queue
+            if (timeoutToExecute != null)
             {
             {
-                lock (_timeoutsLockToken)
+                bool repeat = timeoutToExecute.Callback! ();
+                
+                if (repeat)
                 {
                 {
-                    _timeouts.Add (NudgeToUniqueKey (k), timeout);
+                    AddTimeout (timeoutToExecute.Span, timeoutToExecute);
                 }
                 }
             }
             }
         }
         }

+ 1 - 1
Terminal.Gui/Drawing/Cell.cs

@@ -27,7 +27,7 @@ public record struct Cell (Attribute? Attribute = null, bool IsDirty = false, st
         readonly get => _grapheme;
         readonly get => _grapheme;
         set
         set
         {
         {
-            if (GraphemeHelper.GetGraphemes(value).ToArray().Length > 1)
+            if (GraphemeHelper.GetGraphemeCount (value) > 1)
             {
             {
                 throw new InvalidOperationException ($"Only a single {nameof (Grapheme)} cluster is allowed per Cell.");
                 throw new InvalidOperationException ($"Only a single {nameof (Grapheme)} cluster is allowed per Cell.");
             }
             }

+ 0 - 68
Terminal.Gui/Drawing/Color/AnsiColorNameResolver.cs

@@ -1,68 +0,0 @@
-using System.Collections.Immutable;
-using System.Diagnostics.CodeAnalysis;
-
-namespace Terminal.Gui.Drawing;
-
-/// <summary>
-/// Color name resolver for <see cref="ColorName16"/>.
-/// </summary>
-public class AnsiColorNameResolver : IColorNameResolver
-{
-    private static readonly ImmutableArray<string> _ansiColorNames = ImmutableArray.Create (Enum.GetNames<ColorName16> ());
-
-    /// <inheritdoc/>
-    public IEnumerable<string> GetColorNames ()
-    {
-        return _ansiColorNames;
-    }
-
-    /// <inheritdoc/>
-    public bool TryNameColor (Color color, [NotNullWhen (true)] out string? name)
-    {
-        if (Color.TryGetExactNamedColor16 (color, out ColorName16 colorName16))
-        {
-            name = Color16Name (colorName16);
-            return true;
-        }
-        name = null;
-        return false;
-    }
-
-    /// <inheritdoc/>
-    public bool TryParseColor (ReadOnlySpan<char> name, out Color color)
-    {
-        if (Enum.TryParse (name, ignoreCase: true, out ColorName16 colorName16) &&
-            // Any numerical value converts to undefined enum value.
-            Enum.IsDefined (colorName16))
-        {
-            color = new Color (colorName16);
-            return true;
-        }
-        color = default;
-        return false;
-    }
-
-    private static string Color16Name (ColorName16 color16)
-    {
-        return color16 switch
-        {
-            ColorName16.Black => nameof (ColorName16.Black),
-            ColorName16.Blue => nameof (ColorName16.Blue),
-            ColorName16.Green => nameof (ColorName16.Green),
-            ColorName16.Cyan => nameof (ColorName16.Cyan),
-            ColorName16.Red => nameof (ColorName16.Red),
-            ColorName16.Magenta => nameof (ColorName16.Magenta),
-            ColorName16.Yellow => nameof (ColorName16.Yellow),
-            ColorName16.Gray => nameof (ColorName16.Gray),
-            ColorName16.DarkGray => nameof (ColorName16.DarkGray),
-            ColorName16.BrightBlue => nameof (ColorName16.BrightBlue),
-            ColorName16.BrightGreen => nameof (ColorName16.BrightGreen),
-            ColorName16.BrightCyan => nameof (ColorName16.BrightCyan),
-            ColorName16.BrightRed => nameof (ColorName16.BrightRed),
-            ColorName16.BrightMagenta => nameof (ColorName16.BrightMagenta),
-            ColorName16.BrightYellow => nameof (ColorName16.BrightYellow),
-            ColorName16.White => nameof (ColorName16.White),
-            _ => throw new NotSupportedException ($"ColorName16 '{color16}' is not supported.")
-        };
-    }
-}

+ 34 - 50
Terminal.Gui/Drawing/Color/Color.cs

@@ -11,8 +11,28 @@ namespace Terminal.Gui.Drawing;
 
 
 /// <summary>
 /// <summary>
 ///     Represents a 24-bit color encoded in ARGB32 format.
 ///     Represents a 24-bit color encoded in ARGB32 format.
-///     <para/>
+///     <para>
+///         The RGB components define the color identity (what color it is), while the alpha channel defines
+///         rendering intent (how transparent it should be when drawn).
+///     </para>
 /// </summary>
 /// </summary>
+/// <remarks>
+///     <para>
+///         When matching colors to standard color names (e.g., via <see cref="ColorStrings.GetColorName"/>),
+///         the alpha channel is ignored. This means colors with the same RGB values but different alpha values
+///         will resolve to the same color name. This design supports transparency features while maintaining
+///         semantic color identity.
+///     </para>
+///     <para>
+///         While Terminal.Gui does not currently support alpha blending during rendering, the alpha channel
+///         is used to indicate rendering intent:
+///         <list type="bullet">
+///             <item><description>Alpha = 0: Fully transparent (don't render)</description></item>
+///             <item><description>Alpha = 255: Fully opaque (normal rendering)</description></item>
+///             <item><description>Other values: Reserved for future alpha blending support</description></item>
+///         </list>
+///     </para>
+/// </remarks>
 /// <seealso cref="Attribute"/>
 /// <seealso cref="Attribute"/>
 /// <seealso cref="ColorExtensions"/>
 /// <seealso cref="ColorExtensions"/>
 /// <seealso cref="ColorName16"/>
 /// <seealso cref="ColorName16"/>
@@ -23,8 +43,16 @@ public readonly partial record struct Color : ISpanParsable<Color>, IUtf8SpanPar
 {
 {
     /// <summary>The value of the alpha channel component</summary>
     /// <summary>The value of the alpha channel component</summary>
     /// <remarks>
     /// <remarks>
-    ///     The alpha channel is not currently supported, so the value of the alpha channel bits will not affect
-    ///     rendering.
+    ///     <para>
+    ///         The alpha channel represents rendering intent (transparency) rather than color identity.
+    ///         Terminal.Gui does not currently perform alpha blending, but uses this value to determine
+    ///         whether to render the color at all (alpha = 0 means don't render).
+    ///     </para>
+    ///     <para>
+    ///         When matching colors to standard color names, the alpha channel is ignored. For example,
+    ///         <c>new Color(255, 0, 0, 255)</c> and <c>new Color(255, 0, 0, 128)</c> will both be
+    ///         identified as "Red".
+    ///     </para>
     /// </remarks>
     /// </remarks>
     [JsonIgnore]
     [JsonIgnore]
     [field: FieldOffset (3)]
     [field: FieldOffset (3)]
@@ -32,8 +60,8 @@ public readonly partial record struct Color : ISpanParsable<Color>, IUtf8SpanPar
 
 
     /// <summary>The value of this <see cref="Color"/> as a <see langword="uint"/> in ARGB32 format.</summary>
     /// <summary>The value of this <see cref="Color"/> as a <see langword="uint"/> in ARGB32 format.</summary>
     /// <remarks>
     /// <remarks>
-    ///     The alpha channel is not currently supported, so the value of the alpha channel bits will not affect
-    ///     rendering.
+    ///     The alpha channel in the ARGB value represents rendering intent (transparency), not color identity.
+    ///     When matching to standard color names, only the RGB components are considered.
     /// </remarks>
     /// </remarks>
     [JsonIgnore]
     [JsonIgnore]
     [field: FieldOffset (0)]
     [field: FieldOffset (0)]
@@ -134,8 +162,6 @@ public readonly partial record struct Color : ISpanParsable<Color>, IUtf8SpanPar
     /// <summary>Initializes a new instance of the <see cref="Color"/> with all channels set to 0.</summary>
     /// <summary>Initializes a new instance of the <see cref="Color"/> with all channels set to 0.</summary>
     public Color () { Argb = 0u; }
     public Color () { Argb = 0u; }
 
 
-    // TODO: ColorName and AnsiColorCode are only needed when a driver is in Force16Color mode and we
-    // TODO: should be able to remove these from any non-Driver-specific usages.
     /// <summary>Gets or sets the 3-byte/6-character hexadecimal value for each of the legacy 16-color values.</summary>
     /// <summary>Gets or sets the 3-byte/6-character hexadecimal value for each of the legacy 16-color values.</summary>
     [ConfigurationProperty (Scope = typeof (SettingsScope), OmitClassName = true)]
     [ConfigurationProperty (Scope = typeof (SettingsScope), OmitClassName = true)]
     public static Dictionary<ColorName16, string> Colors16
     public static Dictionary<ColorName16, string> Colors16
@@ -203,31 +229,6 @@ public readonly partial record struct Color : ISpanParsable<Color>, IUtf8SpanPar
     [MethodImpl (MethodImplOptions.AggressiveInlining)]
     [MethodImpl (MethodImplOptions.AggressiveInlining)]
     public bool IsClosestToNamedColor16 (in ColorName16 namedColor) { return GetClosestNamedColor16 () == namedColor; }
     public bool IsClosestToNamedColor16 (in ColorName16 namedColor) { return GetClosestNamedColor16 () == namedColor; }
 
 
-    /// <summary>
-    ///     Determines if the closest named <see cref="Color"/> to <paramref name="color"/>/> is the provided
-    ///     <paramref name="namedColor"/>.
-    /// </summary>
-    /// <param name="color">
-    ///     The color to test against the <see cref="GetClosestNamedColor16(Color)"/> value in
-    ///     <paramref name="namedColor"/>.
-    /// </param>
-    /// <param name="namedColor">
-    ///     The <see cref="GetClosestNamedColor16(Color)"/> to check if this <see cref="Color"/> is closer
-    ///     to than any other configured named color.
-    /// </param>
-    /// <returns>
-    ///     <see langword="true"/> if the closest named color to <paramref name="color"/> is the provided value. <br/>
-    ///     <see langword="false"/> if any other named color is closer to <paramref name="color"/> than
-    ///     <paramref name="namedColor"/>.
-    /// </returns>
-    /// <remarks>
-    ///     If <paramref name="color"/> is equidistant from two named colors, the result of this method is not guaranteed
-    ///     to be determinate.
-    /// </remarks>
-    [Pure]
-    [MethodImpl (MethodImplOptions.AggressiveInlining)]
-    public static bool IsColorClosestToNamedColor16 (in Color color, in ColorName16 namedColor) { return color.IsClosestToNamedColor16 (in namedColor); }
-
     /// <summary>Gets the "closest" named color to this <see cref="Color"/> value.</summary>
     /// <summary>Gets the "closest" named color to this <see cref="Color"/> value.</summary>
     /// <param name="inputColor"></param>
     /// <param name="inputColor"></param>
     /// <remarks>
     /// <remarks>
@@ -240,15 +241,6 @@ public readonly partial record struct Color : ISpanParsable<Color>, IUtf8SpanPar
         return ColorExtensions.ColorToName16Map!.MinBy (pair => CalculateColorDistance (inputColor, pair.Key)).Value;
         return ColorExtensions.ColorToName16Map!.MinBy (pair => CalculateColorDistance (inputColor, pair.Key)).Value;
     }
     }
 
 
-    /// <summary>Converts the given color value to exact named color represented by <see cref="ColorName16"/>.</summary>
-    /// <param name="inputColor"></param>
-    /// <param name="colorName16">Successfully converted named color.</param>
-    /// <returns>True if conversion succeeded; otherwise false.</returns>
-    internal static bool TryGetExactNamedColor16 (Color inputColor, out ColorName16 colorName16)
-    {
-        return ColorExtensions.ColorToName16Map!.TryGetValue (inputColor, out colorName16);
-    }
-
     [SkipLocalsInit]
     [SkipLocalsInit]
     private static float CalculateColorDistance (in Vector4 color1, in Vector4 color2) { return Vector4.Distance (color1, color2); }
     private static float CalculateColorDistance (in Vector4 color1, in Vector4 color2) { return Vector4.Distance (color1, color2); }
 
 
@@ -297,16 +289,8 @@ public readonly partial record struct Color : ISpanParsable<Color>, IUtf8SpanPar
         HSL? hsl = ColorConverter.RgbToHsl (new (R, G, B));
         HSL? hsl = ColorConverter.RgbToHsl (new (R, G, B));
 
 
         double lNorm = hsl.L / 255.0;
         double lNorm = hsl.L / 255.0;
-        double newL;
 
 
-        if (lNorm < 0.5)
-        {
-            newL = Math.Min (1.0, lNorm + brightenAmount);
-        }
-        else
-        {
-            newL = Math.Max (0.0, lNorm - brightenAmount);
-        }
+        double newL = lNorm < 0.5 ? Math.Min (1.0, lNorm + brightenAmount) : Math.Max (0.0, lNorm - brightenAmount);
 
 
         if (Math.Abs (newL - lNorm) < 0.1)
         if (Math.Abs (newL - lNorm) < 0.1)
         {
         {

+ 6 - 5
Terminal.Gui/Drawing/Color/ColorModel.cs

@@ -1,23 +1,24 @@
+// ReSharper disable InconsistentNaming
+
 namespace Terminal.Gui.Drawing;
 namespace Terminal.Gui.Drawing;
 
 
 /// <summary>
 /// <summary>
-/// Describes away of modelling color e.g. Hue
-/// Saturation Lightness.
+///     Describes a way of modelling color e.g. Hue, Saturation, and Lightness.
 /// </summary>
 /// </summary>
 public enum ColorModel
 public enum ColorModel
 {
 {
     /// <summary>
     /// <summary>
-    /// Color modelled by storing Red, Green and Blue as (0-255) ints
+    ///     Color modelled by storing Red, Green and Blue as (0-255) ints
     /// </summary>
     /// </summary>
     RGB,
     RGB,
 
 
     /// <summary>
     /// <summary>
-    /// Color modelled by storing Hue (360 degrees), Saturation (100%) and Value (100%)
+    ///     Color modelled by storing Hue (360 degrees), Saturation (100%) and Value (100%)
     /// </summary>
     /// </summary>
     HSV,
     HSV,
 
 
     /// <summary>
     /// <summary>
-    /// Color modelled by storing Hue (360 degrees), Saturation (100%) and Lightness (100%)
+    ///     Color modelled by storing Hue (360 degrees), Saturation (100%) and Lightness (100%)
     /// </summary>
     /// </summary>
     HSL
     HSL
 }
 }

+ 3 - 3
Terminal.Gui/Drawing/Color/ColorQuantizer.cs

@@ -39,7 +39,7 @@ public class ColorQuantizer
     /// <param name="pixels"></param>
     /// <param name="pixels"></param>
     public void BuildPalette (Color [,] pixels)
     public void BuildPalette (Color [,] pixels)
     {
     {
-        List<Color> allColors = new ();
+        List<Color> allColors = [];
         int width = pixels.GetLength (0);
         int width = pixels.GetLength (0);
         int height = pixels.GetLength (1);
         int height = pixels.GetLength (1);
 
 
@@ -56,8 +56,8 @@ public class ColorQuantizer
     }
     }
 
 
     /// <summary>
     /// <summary>
-    /// Returns the closest color in <see cref="Palette"/> that matches <paramref name="toTranslate"/>
-    /// based on the color comparison algorithm defined by <see cref="DistanceAlgorithm"/>
+    ///     Returns the closest color in <see cref="Palette"/> that matches <paramref name="toTranslate"/>
+    ///     based on the color comparison algorithm defined by <see cref="DistanceAlgorithm"/>
     /// </summary>
     /// </summary>
     /// <param name="toTranslate"></param>
     /// <param name="toTranslate"></param>
     /// <returns></returns>
     /// <returns></returns>

+ 6 - 53
Terminal.Gui/Drawing/Color/ColorStrings.cs

@@ -7,47 +7,16 @@ namespace Terminal.Gui.Drawing;
 /// </summary>
 /// </summary>
 public static class ColorStrings
 public static class ColorStrings
 {
 {
-    private static readonly AnsiColorNameResolver _ansi = new();
     private static readonly StandardColorsNameResolver _standard = new();
     private static readonly StandardColorsNameResolver _standard = new();
-    private static readonly MultiStandardColorNameResolver _multi = new();
 
 
     /// <summary>
     /// <summary>
-    ///     Gets the W3C+  standard string for <paramref name="color"/>.
-    /// </summary>
-    /// <param name="color">The color.</param>
-    /// <returns><see langword="null"/> if there is no standard color name for the specified color.</returns>
-    public static string? GetStandardColorName (Color color)
-    {
-        if (_standard.TryNameColor (color, out string? name))
-        {
-            return name;
-        }
-        return null;
-    }
-
-    /// <summary>
-    ///     Gets the ANSI 4-bit (16) color name for <paramref name="color"/>.
-    /// </summary>
-    /// <param name="color">The color.</param>
-    /// <returns><see langword="null"/> if there is no standard color name for the specified color.</returns>
-    // ReSharper disable once InconsistentNaming
-    public static string? GetANSIColor16Name (Color color)
-    {
-        if (_ansi.TryNameColor (color, out string? name))
-        {
-            return name;
-        }
-        return null;
-    }
-
-    /// <summary>
-    ///     Gets backwards compatible color name for <paramref name="color"/>.
+    ///     Gets the color name for <paramref name="color"/>.
     /// </summary>
     /// </summary>
     /// <param name="color">The color.</param>
     /// <param name="color">The color.</param>
     /// <returns>Standard color name for the specified color; otherwise <see langword="null"/>.</returns>
     /// <returns>Standard color name for the specified color; otherwise <see langword="null"/>.</returns>
     public static string? GetColorName (Color color)
     public static string? GetColorName (Color color)
     {
     {
-        if (_multi.TryNameColor (color, out string? name))
+        if (_standard.TryNameColor (color, out string? name))
         {
         {
             return name;
             return name;
         }
         }
@@ -80,30 +49,14 @@ public static class ColorStrings
     }
     }
 
 
     /// <summary>
     /// <summary>
-    ///     Parses <paramref name="name"/> and returns <paramref name="color"/> if name is a ANSI 4-bit standard named color.
-    /// </summary>
-    /// <param name="name">The name to parse.</param>
-    /// <param name="color">If successful, the color.</param>
-    /// <returns><see langword="true"/> if <paramref name="name"/> was parsed successfully.</returns>
-    public static bool TryParseColor16 (ReadOnlySpan<char> name, out Color color)
-    {
-        if (_ansi.TryParseColor (name, out color))
-        {
-            return true;
-        }
-        color = default;
-        return false;
-    }
-
-    /// <summary>
-    ///     Parses <paramref name="name"/> and returns <paramref name="color"/> if name is either ANSI 4-bit or W3C standard named color.
+    ///     Parses <paramref name="name"/> and returns <paramref name="color"/> if name is a W3C+ standard named color.
     /// </summary>
     /// </summary>
     /// <param name="name">The name to parse.</param>
     /// <param name="name">The name to parse.</param>
     /// <param name="color">If successful, the color.</param>
     /// <param name="color">If successful, the color.</param>
     /// <returns><see langword="true"/> if <paramref name="name"/> was parsed successfully.</returns>
     /// <returns><see langword="true"/> if <paramref name="name"/> was parsed successfully.</returns>
     public static bool TryParseNamedColor (ReadOnlySpan<char> name, out Color color)
     public static bool TryParseNamedColor (ReadOnlySpan<char> name, out Color color)
     {
     {
-        if (_multi.TryParseColor (name, out color))
+        if (_standard.TryParseColor (name, out color))
         {
         {
             return true;
             return true;
         }
         }
@@ -113,7 +66,7 @@ public static class ColorStrings
             return true;
             return true;
         }
         }
 
 
-        color = default;
+        color = default (Color);
         return false;
         return false;
     }
     }
 
 
@@ -130,7 +83,7 @@ public static class ColorStrings
             }
             }
         }
         }
 
 
-        color = default;
+        color = default (Color);
         return false;
         return false;
     }
     }
 }
 }

+ 0 - 83
Terminal.Gui/Drawing/Color/MultiStandardColorNameResolver.cs

@@ -1,83 +0,0 @@
-using System.Collections.Frozen;
-using System.Collections.Immutable;
-using System.Diagnostics.CodeAnalysis;
-using System.Globalization;
-
-namespace Terminal.Gui.Drawing;
-
-/// <summary>
-/// Color name resolver prioritizing Standard (W3C+) colors with fallback to ANSI 4-bit (16) colors.
-/// </summary>
-public class MultiStandardColorNameResolver : IColorNameResolver
-{
-    private static readonly AnsiColorNameResolver _ansi = new ();
-    private static readonly StandardColorsNameResolver _standard = new ();
-
-    private static readonly ImmutableArray<string> _combinedColorNames;
-    private static readonly FrozenDictionary<string, Color> _nameToColorMap;
-    private static readonly FrozenDictionary<uint, string> _colorToNameMap;
-
-    static MultiStandardColorNameResolver ()
-    {
-        Dictionary<string, Color> nameToColor = new (StringComparer.OrdinalIgnoreCase);
-        Dictionary<uint, string> colorToName = new ();
-
-        foreach (string name in _standard.GetColorNames ())
-        {
-            if (_standard.TryParseColor (name, out Color color))
-            {
-                if (nameToColor.TryAdd (name, color))
-                {
-                    _ = colorToName.TryAdd (color.Argb, name);
-                }
-            }
-        }
-
-        foreach (string name in _ansi.GetColorNames ())
-        {
-            if (_ansi.TryParseColor (name, out Color color))
-            {
-                nameToColor.TryAdd (name, color);
-                colorToName.TryAdd (color.Argb, name);
-            }
-        }
-
-        _nameToColorMap = nameToColor.ToFrozenDictionary ();
-        _colorToNameMap = colorToName.ToFrozenDictionary ();
-        _combinedColorNames = nameToColor.Keys.Order ().ToImmutableArray ();
-    }
-
-    /// <inheritdoc/>
-    public IEnumerable<string> GetColorNames () => _combinedColorNames;
-
-    /// <inheritdoc/>
-    public bool TryParseColor (ReadOnlySpan<char> name, out Color color)
-    {
-        if (name.StartsWith ("#") || name.StartsWith ("0x", StringComparison.OrdinalIgnoreCase))
-        {
-            try
-            {
-                color = Color.Parse (name.ToString (), CultureInfo.InvariantCulture);
-                return true;
-            }
-            catch
-            {
-                color = default;
-                return false;
-            }
-        }
-
-        if (_ansi.TryParseColor (name, out color)) return true;
-        if (_standard.TryParseColor (name, out color)) return true;
-
-        color = default;
-        return false;
-    }
-
-
-    /// <inheritdoc/>
-    public bool TryNameColor (Color color, [NotNullWhen (true)] out string? name)
-    {
-        return _colorToNameMap.TryGetValue (color.Argb, out name);
-    }
-}

+ 60 - 1
Terminal.Gui/Drawing/Color/StandardColor.cs

@@ -1248,5 +1248,64 @@ public enum StandardColor
     ///         A bright yellowish-green color.
     ///         A bright yellowish-green color.
     ///     </para>
     ///     </para>
     /// </summary>
     /// </summary>
-    YellowGreen = 0x9ACD32
+    YellowGreen = 0x9ACD32,
+
+    // Legacy 16-color names for backwards compatibility
+    // These match the RGB values used in ColorName16 mapping
+
+    /// <summary>
+    ///     Bright Black RGB(118, 118, 118).
+    ///     <para>
+    ///         A bright black (dark gray) color from the legacy 16-color palette. This is the ColorName16.DarkGray color.
+    ///     </para>
+    /// </summary>
+    BrightBlack = 0x767676,
+
+    /// <summary>
+    ///     Bright Blue RGB(59, 120, 255).
+    ///     <para>
+    ///         A bright blue color from the legacy 16-color palette.
+    ///     </para>
+    /// </summary>
+    BrightBlue = 0x3B78FF,
+
+    /// <summary>
+    ///     Bright Cyan RGB(97, 214, 214).
+    ///     <para>
+    ///         A bright cyan color from the legacy 16-color palette.
+    ///     </para>
+    /// </summary>
+    BrightCyan = 0x61D6D6,
+
+    /// <summary>
+    ///     Bright Green RGB(22, 198, 12).
+    ///     <para>
+    ///         A bright green color from the legacy 16-color palette.
+    ///     </para>
+    /// </summary>
+    BrightGreen = 0x16C60C,
+
+    /// <summary>
+    ///     Bright Magenta RGB(180, 0, 158).
+    ///     <para>
+    ///         A bright magenta color from the legacy 16-color palette.
+    ///     </para>
+    /// </summary>
+    BrightMagenta = 0xB4009E,
+
+    /// <summary>
+    ///     Bright Red RGB(231, 72, 86).
+    ///     <para>
+    ///         A bright red color from the legacy 16-color palette.
+    ///     </para>
+    /// </summary>
+    BrightRed = 0xE74856,
+
+    /// <summary>
+    ///     Bright Yellow RGB(249, 241, 165).
+    ///     <para>
+    ///         A bright yellow color from the legacy 16-color palette.
+    ///     </para>
+    /// </summary>
+    BrightYellow = 0xF9F1A5
 }
 }

+ 5 - 2
Terminal.Gui/Drawing/Color/StandardColors.cs

@@ -18,7 +18,7 @@ internal static class StandardColors
     {
     {
         string [] standardNames = Enum.GetNames<StandardColor> ().Order ().ToArray ();
         string [] standardNames = Enum.GetNames<StandardColor> ().Order ().ToArray ();
 
 
-        return ImmutableArray.Create (standardNames);
+        return [.. standardNames];
     }
     }
 
 
     private static readonly Lazy<FrozenDictionary<uint, string>> _argbNameMap = new (
     private static readonly Lazy<FrozenDictionary<uint, string>> _argbNameMap = new (
@@ -82,7 +82,10 @@ internal static class StandardColors
     /// <returns>True if conversion succeeded; otherwise false.</returns>
     /// <returns>True if conversion succeeded; otherwise false.</returns>
     public static bool TryNameColor (Color color, [NotNullWhen (true)] out string? name)
     public static bool TryNameColor (Color color, [NotNullWhen (true)] out string? name)
     {
     {
-        if (_argbNameMap.Value.TryGetValue (color.Argb, out name))
+        // Ignore alpha channel when matching - alpha represents transparency, not color identity
+        uint opaqueArgb = color.Argb | 0xFF000000;
+
+        if (_argbNameMap.Value.TryGetValue (opaqueArgb, out name))
         {
         {
             return true;
             return true;
         }
         }

+ 23 - 0
Terminal.Gui/Drawing/GraphemeHelper.cs

@@ -46,4 +46,27 @@ public static class GraphemeHelper
             yield return element;
             yield return element;
         }
         }
     }
     }
+
+    /// <summary>
+    ///     Counts the number of grapheme clusters in a string without allocating intermediate collections.
+    /// </summary>
+    /// <param name="text">The string to count graphemes in.</param>
+    /// <returns>The number of grapheme clusters, or 0 if the string is null or empty.</returns>
+    public static int GetGraphemeCount (string text)
+    {
+        if (string.IsNullOrEmpty (text))
+        {
+            return 0;
+        }
+
+        TextElementEnumerator enumerator = StringInfo.GetTextElementEnumerator (text);
+        var count = 0;
+
+        while (enumerator.MoveNext ())
+        {
+            count++;
+        }
+
+        return count;
+    }
 }
 }

+ 12 - 4
Terminal.Gui/Drawing/LineCanvas/LineCanvas.cs

@@ -211,15 +211,23 @@ public class LineCanvas : IDisposable
     {
     {
         Dictionary<Point, Rune> map = new ();
         Dictionary<Point, Rune> map = new ();
 
 
+        List<IntersectionDefinition> intersectionsBufferList = [];
+
         // walk through each pixel of the bitmap
         // walk through each pixel of the bitmap
         for (int y = inArea.Y; y < inArea.Y + inArea.Height; y++)
         for (int y = inArea.Y; y < inArea.Y + inArea.Height; y++)
         {
         {
             for (int x = inArea.X; x < inArea.X + inArea.Width; x++)
             for (int x = inArea.X; x < inArea.X + inArea.Width; x++)
             {
             {
-                IntersectionDefinition [] intersects = _lines
-                    .Select (l => l.Intersects (x, y))
-                    .OfType<IntersectionDefinition> () // automatically filters nulls and casts
-                    .ToArray ();
+                intersectionsBufferList.Clear ();
+                foreach (var line in _lines)
+                {
+                    if (line.Intersects (x, y) is { } intersect)
+                    {
+                        intersectionsBufferList.Add (intersect);
+                    }
+                }
+                // Safe as long as the list is not modified while the span is in use.
+                ReadOnlySpan<IntersectionDefinition> intersects = CollectionsMarshal.AsSpan(intersectionsBufferList);
 
 
                 Rune? rune = GetRuneForIntersects (intersects);
                 Rune? rune = GetRuneForIntersects (intersects);
 
 

+ 5 - 4
Terminal.Gui/Drawing/Sixel/SixelSupportDetector.cs

@@ -32,8 +32,9 @@ public class SixelSupportDetector ()
     /// </returns>
     /// </returns>
     public void Detect (Action<SixelSupportResult> resultCallback)
     public void Detect (Action<SixelSupportResult> resultCallback)
     {
     {
-        var result = new SixelSupportResult ();
-        result.SupportsTransparency = IsVirtualTerminal () || IsXtermWithTransparency ();
+        SixelSupportResult result = new SixelSupportResult ();
+        bool isLegacyConsole = IsLegacyConsole ();
+        result.SupportsTransparency = !isLegacyConsole || (!isLegacyConsole && IsXtermWithTransparency ());
         IsSixelSupportedByDar (result, resultCallback);
         IsSixelSupportedByDar (result, resultCallback);
     }
     }
 
 
@@ -155,9 +156,9 @@ public class SixelSupportDetector ()
 
 
     private static bool ResponseIndicatesSupport (string response) { return response.Split (';').Contains ("4"); }
     private static bool ResponseIndicatesSupport (string response) { return response.Split (';').Contains ("4"); }
 
 
-    private static bool IsVirtualTerminal ()
+    private bool IsLegacyConsole ()
     {
     {
-        return !string.IsNullOrWhiteSpace (Environment.GetEnvironmentVariable ("WT_SESSION"));
+        return _driver is { IsLegacyConsole: true };
     }
     }
 
 
     private static bool IsXtermWithTransparency ()
     private static bool IsXtermWithTransparency ()

+ 1 - 1
Terminal.Gui/Drivers/DotNetDriver/NetOutput.cs

@@ -81,7 +81,7 @@ public class NetOutput : OutputBase, IOutput
     /// <inheritdoc/>
     /// <inheritdoc/>
     protected override void AppendOrWriteAttribute (StringBuilder output, Attribute attr, TextStyle redrawTextStyle)
     protected override void AppendOrWriteAttribute (StringBuilder output, Attribute attr, TextStyle redrawTextStyle)
     {
     {
-        if (Application.Force16Colors)
+        if (Force16Colors)
         {
         {
             output.Append (EscSeqUtils.CSI_SetForegroundColor (attr.Foreground.GetAnsiColorCode ()));
             output.Append (EscSeqUtils.CSI_SetForegroundColor (attr.Foreground.GetAnsiColorCode ()));
             output.Append (EscSeqUtils.CSI_SetBackgroundColor (attr.Background.GetAnsiColorCode ()));
             output.Append (EscSeqUtils.CSI_SetBackgroundColor (attr.Background.GetAnsiColorCode ()));

+ 31 - 0
Terminal.Gui/Drivers/Driver.cs

@@ -0,0 +1,31 @@
+namespace Terminal.Gui.Drivers;
+
+/// <summary>
+///     Holds global driver settings.
+/// </summary>
+public sealed class Driver
+{
+    private static bool _force16Colors = false; // Resources/config.json overrides
+
+    // NOTE: Force16Colors is a configuration property (Driver.Force16Colors).
+    // NOTE: IDriver also has a Force16Colors property, which is an instance property
+    // NOTE: set whenever this static property is set.
+    /// <summary>
+    ///     Determines if driver instances should use 16 colors instead of the default TrueColors.
+    /// </summary>
+    /// <seealso cref="IDriver.Force16Colors"/>
+    [ConfigurationProperty (Scope = typeof (SettingsScope))]
+    public static bool Force16Colors
+    {
+        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;
+}

+ 181 - 163
Terminal.Gui/Drivers/DriverImpl.cs

@@ -1,4 +1,5 @@
-using System.Runtime.InteropServices;
+using System.Collections.Concurrent;
+using System.Runtime.InteropServices;
 
 
 namespace Terminal.Gui.Drivers;
 namespace Terminal.Gui.Drivers;
 
 
@@ -28,10 +29,6 @@ namespace Terminal.Gui.Drivers;
 /// </remarks>
 /// </remarks>
 internal class DriverImpl : IDriver
 internal class DriverImpl : IDriver
 {
 {
-    private readonly IOutput _output;
-    private readonly AnsiRequestScheduler _ansiRequestScheduler;
-    private CursorVisibility _lastCursor = CursorVisibility.Default;
-
     /// <summary>
     /// <summary>
     ///     Initializes a new instance of the <see cref="DriverImpl"/> class.
     ///     Initializes a new instance of the <see cref="DriverImpl"/> class.
     /// </summary>
     /// </summary>
@@ -63,19 +60,88 @@ internal class DriverImpl : IDriver
                                      };
                                      };
 
 
         SizeMonitor = sizeMonitor;
         SizeMonitor = sizeMonitor;
+        SizeMonitor.SizeChanged += OnSizeMonitorOnSizeChanged;
 
 
-        sizeMonitor.SizeChanged += (_, e) =>
-                                   {
-                                       SetScreenSize (e.Size!.Value.Width, e.Size.Value.Height);
+        CreateClipboard ();
 
 
-                                       //SizeChanged?.Invoke (this, e);
-                                   };
+        Driver.Force16ColorsChanged += OnDriverOnForce16ColorsChanged;
+    }
 
 
-        CreateClipboard ();
+    #region Driver Lifecycle
+
+    /// <inheritdoc/>
+    public void Init () { throw new NotSupportedException (); }
+
+    /// <inheritdoc/>
+    public void Refresh () { _output.Write (OutputBuffer); }
+
+    /// <inheritdoc/>
+    public string? GetName () => InputProcessor.DriverName?.ToLowerInvariant ();
+
+    /// <inheritdoc/>
+    public virtual string GetVersionInfo ()
+    {
+        string type = InputProcessor.DriverName ?? throw new ArgumentNullException (nameof (InputProcessor.DriverName));
+
+        return type;
     }
     }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public event EventHandler<SizeChangedEventArgs>? SizeChanged;
+    public void Suspend ()
+    {
+        // BUGBUG: This is all platform-specific and should not be implemented here.
+        // BUGBUG: This needs to be in each platform's driver implementation.
+        if (Environment.OSVersion.Platform != PlatformID.Unix)
+        {
+            return;
+        }
+
+        Console.Out.Write (EscSeqUtils.CSI_DisableMouseEvents);
+
+        try
+        {
+            Console.ResetColor ();
+            Console.Clear ();
+
+            //Disable alternative screen buffer.
+            Console.Out.Write (EscSeqUtils.CSI_RestoreCursorAndRestoreAltBufferWithBackscroll);
+
+            //Set cursor key to cursor.
+            Console.Out.Write (EscSeqUtils.CSI_ShowCursor);
+
+            Platform.Suspend ();
+
+            //Enable alternative screen buffer.
+            Console.Out.Write (EscSeqUtils.CSI_SaveCursorAndActivateAltBufferNoBackscroll);
+        }
+        catch (Exception ex)
+        {
+            Logging.Error ($"Error suspending terminal: {ex.Message}");
+        }
+
+        Console.Out.Write (EscSeqUtils.CSI_EnableMouseEvents);
+    }
+
+    /// <inheritdoc/>
+    public bool IsLegacyConsole
+    {
+        get => _output.IsLegacyConsole;
+        set => _output.IsLegacyConsole = value;
+    }
+
+    /// <inheritdoc/>
+    public void Dispose ()
+    {
+        SizeMonitor.SizeChanged -= OnSizeMonitorOnSizeChanged;
+        Driver.Force16ColorsChanged -= OnDriverOnForce16ColorsChanged;
+        _output.Dispose ();
+    }
+
+    #endregion Driver Lifecycle
+
+    #region Driver Components
+
+    private readonly IOutput _output;
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
     public IInputProcessor InputProcessor { get; }
     public IInputProcessor InputProcessor { get; }
@@ -86,6 +152,9 @@ internal class DriverImpl : IDriver
     /// <inheritdoc/>
     /// <inheritdoc/>
     public ISizeMonitor SizeMonitor { get; }
     public ISizeMonitor SizeMonitor { get; }
 
 
+    /// <inheritdoc/>
+    public IClipboard? Clipboard { get; private set; } = new FakeClipboard ();
+
     private void CreateClipboard ()
     private void CreateClipboard ()
     {
     {
         if (InputProcessor.DriverName is { } && InputProcessor.DriverName.Contains ("fake"))
         if (InputProcessor.DriverName is { } && InputProcessor.DriverName.Contains ("fake"))
@@ -116,16 +185,12 @@ internal class DriverImpl : IDriver
         // Clipboard is set to FakeClipboard at initialization
         // Clipboard is set to FakeClipboard at initialization
     }
     }
 
 
-    /// <inheritdoc/>
+    #endregion Driver Components
 
 
-    public Rectangle Screen =>
+    #region Screen and Display
 
 
-        //if (Application.RunningUnitTests && _output is WindowsConsoleOutput or NetOutput)
-        //{
-        //    // In unit tests, we don't have a real output, so we return an empty rectangle.
-        //    return Rectangle.Empty;
-        //}
-        new (0, 0, OutputBuffer.Cols, OutputBuffer.Rows);
+    /// <inheritdoc/>
+    public Rectangle Screen => new (0, 0, OutputBuffer.Cols, OutputBuffer.Rows);
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
     public virtual void SetScreenSize (int width, int height)
     public virtual void SetScreenSize (int width, int height)
@@ -136,23 +201,11 @@ internal class DriverImpl : IDriver
     }
     }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
+    public event EventHandler<SizeChangedEventArgs>? SizeChanged;
 
 
-    public Region? Clip
-    {
-        get => OutputBuffer.Clip;
-        set => OutputBuffer.Clip = value;
-    }
-
-    /// <inheritdoc/>
-
-    public IClipboard? Clipboard { get; private set; } = new FakeClipboard ();
+    private void OnSizeMonitorOnSizeChanged (object? _, SizeChangedEventArgs e) { SetScreenSize (e.Size!.Value.Width, e.Size.Value.Height); }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-
-    public int Col => OutputBuffer.Col;
-
-    /// <inheritdoc/>
-
     public int Cols
     public int Cols
     {
     {
         get => OutputBuffer.Cols;
         get => OutputBuffer.Cols;
@@ -160,15 +213,13 @@ internal class DriverImpl : IDriver
     }
     }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-
-    public Cell [,]? Contents
+    public int Rows
     {
     {
-        get => OutputBuffer.Contents;
-        set => OutputBuffer.Contents = value;
+        get => OutputBuffer.Rows;
+        set => OutputBuffer.Rows = value;
     }
     }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-
     public int Left
     public int Left
     {
     {
         get => OutputBuffer.Left;
         get => OutputBuffer.Left;
@@ -176,55 +227,45 @@ internal class DriverImpl : IDriver
     }
     }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-
-    public int Row => OutputBuffer.Row;
-
-    /// <inheritdoc/>
-
-    public int Rows
-    {
-        get => OutputBuffer.Rows;
-        set => OutputBuffer.Rows = value;
-    }
-
-    /// <inheritdoc/>
-
     public int Top
     public int Top
     {
     {
         get => OutputBuffer.Top;
         get => OutputBuffer.Top;
         set => OutputBuffer.Top = value;
         set => OutputBuffer.Top = value;
     }
     }
 
 
-    // TODO: Probably not everyone right?
-
-    /// <inheritdoc/>
+    #endregion Screen and Display
 
 
-    public bool SupportsTrueColor => true;
+    #region Color Support
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
+    public bool SupportsTrueColor => !IsLegacyConsole;
 
 
+    /// <inheritdoc/>
     public bool Force16Colors
     public bool Force16Colors
     {
     {
-        get => Application.Force16Colors || !SupportsTrueColor;
-        set => Application.Force16Colors = value || !SupportsTrueColor;
+        get => _output.Force16Colors;
+        set => _output.Force16Colors = value;
     }
     }
 
 
-    /// <inheritdoc/>
+    private void OnDriverOnForce16ColorsChanged (object? _, ValueChangedEventArgs<bool> e) { Force16Colors = e.NewValue; }
 
 
-    public Attribute CurrentAttribute
-    {
-        get => OutputBuffer.CurrentAttribute;
-        set => OutputBuffer.CurrentAttribute = value;
-    }
+    #endregion Color Support
 
 
-    /// <inheritdoc/>
-    public void AddRune (Rune rune) { OutputBuffer.AddRune (rune); }
+    #region Content Buffer
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public void AddRune (char c) { OutputBuffer.AddRune (c); }
+    public Cell [,]? Contents
+    {
+        get => OutputBuffer.Contents;
+        set => OutputBuffer.Contents = value;
+    }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public void AddStr (string str) { OutputBuffer.AddStr (str); }
+    public Region? Clip
+    {
+        get => OutputBuffer.Clip;
+        set => OutputBuffer.Clip = value;
+    }
 
 
     /// <summary>Clears the <see cref="IDriver.Contents"/> of the driver.</summary>
     /// <summary>Clears the <see cref="IDriver.Contents"/> of the driver.</summary>
     public void ClearContents ()
     public void ClearContents ()
@@ -236,20 +277,26 @@ internal class DriverImpl : IDriver
     /// <inheritdoc/>
     /// <inheritdoc/>
     public event EventHandler<EventArgs>? ClearedContents;
     public event EventHandler<EventArgs>? ClearedContents;
 
 
+    #endregion Content Buffer
+
+    #region Drawing and Rendering
+
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public void FillRect (Rectangle rect, Rune rune = default) { OutputBuffer.FillRect (rect, rune); }
+    public int Col => OutputBuffer.Col;
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public void FillRect (Rectangle rect, char c) { OutputBuffer.FillRect (rect, c); }
+    public int Row => OutputBuffer.Row;
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public virtual string GetVersionInfo ()
+    public Attribute CurrentAttribute
     {
     {
-        string type = InputProcessor.DriverName ?? throw new ArgumentNullException (nameof (InputProcessor.DriverName));
-
-        return type;
+        get => OutputBuffer.CurrentAttribute;
+        set => OutputBuffer.CurrentAttribute = value;
     }
     }
 
 
+    /// <inheritdoc/>
+    public void Move (int col, int row) { OutputBuffer.Move (col, row); }
+
     /// <inheritdoc/>
     /// <inheritdoc/>
     public bool IsRuneSupported (Rune rune) => Rune.IsValid (rune.Value);
     public bool IsRuneSupported (Rune rune) => Rune.IsValid (rune.Value);
 
 
@@ -262,77 +309,22 @@ internal class DriverImpl : IDriver
     ///     <see cref="IDriver.Clip"/>.
     ///     <see cref="IDriver.Clip"/>.
     ///     <see langword="true"/> otherwise.
     ///     <see langword="true"/> otherwise.
     /// </returns>
     /// </returns>
-    public bool IsValidLocation (string text, int col, int row) { return OutputBuffer.IsValidLocation (text, col, row); }
+    public bool IsValidLocation (string text, int col, int row) => OutputBuffer.IsValidLocation (text, col, row);
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public void Move (int col, int row) { OutputBuffer.Move (col, row); }
-
-    // TODO: Probably part of output
-
-    /// <inheritdoc/>
-    public bool SetCursorVisibility (CursorVisibility visibility)
-    {
-        _lastCursor = visibility;
-        _output.SetCursorVisibility (visibility);
-
-        return true;
-    }
-
-    /// <inheritdoc/>
-    public bool GetCursorVisibility (out CursorVisibility current)
-    {
-        current = _lastCursor;
-
-        return true;
-    }
+    public void AddRune (Rune rune) { OutputBuffer.AddRune (rune); }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public void Suspend ()
-    {
-        // BUGBUG: This is all platform-specific and should not be implemented here.
-        // BUGBUG: This needs to be in each platform's driver implementation.
-        if (Environment.OSVersion.Platform != PlatformID.Unix)
-        {
-            return;
-        }
-
-        Console.Out.Write (EscSeqUtils.CSI_DisableMouseEvents);
-
-        try
-        {
-            Console.ResetColor ();
-            Console.Clear ();
-
-            //Disable alternative screen buffer.
-            Console.Out.Write (EscSeqUtils.CSI_RestoreCursorAndRestoreAltBufferWithBackscroll);
-
-            //Set cursor key to cursor.
-            Console.Out.Write (EscSeqUtils.CSI_ShowCursor);
-
-            Platform.Suspend ();
-
-            //Enable alternative screen buffer.
-            Console.Out.Write (EscSeqUtils.CSI_SaveCursorAndActivateAltBufferNoBackscroll);
-        }
-        catch (Exception ex)
-        {
-            Logging.Error ($"Error suspending terminal: {ex.Message}");
-        }
-
-        Console.Out.Write (EscSeqUtils.CSI_EnableMouseEvents);
-    }
+    public void AddRune (char c) { OutputBuffer.AddRune (c); }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public void UpdateCursor () { _output.SetCursorPosition (Col, Row); }
+    public void AddStr (string str) { OutputBuffer.AddStr (str); }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public void Init () { throw new NotSupportedException (); }
+    public void FillRect (Rectangle rect, Rune rune = default) { OutputBuffer.FillRect (rect, rune); }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public void End ()
-    {
-        // TODO: Nope
-    }
+    public void FillRect (Rectangle rect, char c) { OutputBuffer.FillRect (rect, c); }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
     public Attribute SetAttribute (Attribute newAttribute)
     public Attribute SetAttribute (Attribute newAttribute)
@@ -346,35 +338,11 @@ internal class DriverImpl : IDriver
     /// <inheritdoc/>
     /// <inheritdoc/>
     public Attribute GetAttribute () => OutputBuffer.CurrentAttribute;
     public Attribute GetAttribute () => OutputBuffer.CurrentAttribute;
 
 
-    /// <summary>Event fired when a key is pressed down. This is a precursor to <see cref="IDriver.KeyUp"/>.</summary>
-    public event EventHandler<Key>? KeyDown;
-
-    /// <inheritdoc/>
-    public event EventHandler<Key>? KeyUp;
-
-    /// <summary>Event fired when a mouse event occurs.</summary>
-    public event EventHandler<MouseEventArgs>? MouseEvent;
-
     /// <inheritdoc/>
     /// <inheritdoc/>
     public void WriteRaw (string ansi) { _output.Write (ansi); }
     public void WriteRaw (string ansi) { _output.Write (ansi); }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public void EnqueueKeyEvent (Key key) { InputProcessor.EnqueueKeyDownEvent (key); }
-
-    /// <inheritdoc/>
-    public void QueueAnsiRequest (AnsiEscapeSequenceRequest request) { _ansiRequestScheduler.SendOrSchedule (this, request); }
-
-    /// <inheritdoc/>
-    public AnsiRequestScheduler GetRequestScheduler () => _ansiRequestScheduler;
-
-    /// <inheritdoc/>
-    public void Refresh ()
-    {
-        // No need we will always draw when dirty
-    }
-
-    /// <inheritdoc/>
-    public string? GetName () => InputProcessor.DriverName?.ToLowerInvariant ();
+    public ConcurrentQueue<SixelToRender> GetSixels () => _output.GetSixels ();
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
     public new string ToString ()
     public new string ToString ()
@@ -403,9 +371,59 @@ internal class DriverImpl : IDriver
         return sb.ToString ();
         return sb.ToString ();
     }
     }
 
 
-    /// <inheritdoc />
-    public string ToAnsi ()
+    /// <inheritdoc/>
+    public string ToAnsi () => _output.ToAnsi (OutputBuffer);
+
+    #endregion Drawing and Rendering
+
+    #region Cursor
+
+    private CursorVisibility _lastCursor = CursorVisibility.Default;
+
+    /// <inheritdoc/>
+    public void UpdateCursor () { _output.SetCursorPosition (Col, Row); }
+
+    /// <inheritdoc/>
+    public bool GetCursorVisibility (out CursorVisibility current)
     {
     {
-        return _output.ToAnsi (OutputBuffer);
+        current = _lastCursor;
+
+        return true;
     }
     }
+
+    /// <inheritdoc/>
+    public bool SetCursorVisibility (CursorVisibility visibility)
+    {
+        _lastCursor = visibility;
+        _output.SetCursorVisibility (visibility);
+
+        return true;
+    }
+
+    #endregion Cursor
+
+    #region Input Events
+
+    /// <summary>Event fired when a mouse event occurs.</summary>
+    public event EventHandler<MouseEventArgs>? MouseEvent;
+
+    /// <summary>Event fired when a key is pressed down. This is a precursor to <see cref="IDriver.KeyUp"/>.</summary>
+    public event EventHandler<Key>? KeyDown;
+
+    /// <inheritdoc/>
+    public event EventHandler<Key>? KeyUp;
+
+    /// <inheritdoc/>
+    public void EnqueueKeyEvent (Key key) { InputProcessor.EnqueueKeyDownEvent (key); }
+
+    #endregion Input Events
+
+    #region ANSI Escape Sequences
+
+    private readonly AnsiRequestScheduler _ansiRequestScheduler;
+
+    /// <inheritdoc/>
+    public virtual void QueueAnsiRequest (AnsiEscapeSequenceRequest request) { _ansiRequestScheduler.SendOrSchedule (this, request); }
+
+    #endregion ANSI Escape Sequences
 }
 }

+ 16 - 5
Terminal.Gui/Drivers/FakeDriver/FakeOutput.cs

@@ -86,10 +86,21 @@ public class FakeOutput : OutputBase, IOutput
     /// <inheritdoc/>
     /// <inheritdoc/>
     protected override void AppendOrWriteAttribute (StringBuilder output, Attribute attr, TextStyle redrawTextStyle)
     protected override void AppendOrWriteAttribute (StringBuilder output, Attribute attr, TextStyle redrawTextStyle)
     {
     {
-        if (Application.Force16Colors)
+        if (Force16Colors)
         {
         {
-            output.Append (EscSeqUtils.CSI_SetForegroundColor (attr.Foreground.GetAnsiColorCode ()));
-            output.Append (EscSeqUtils.CSI_SetBackgroundColor (attr.Background.GetAnsiColorCode ()));
+            if (!IsLegacyConsole)
+            {
+                output.Append (EscSeqUtils.CSI_SetForegroundColor (attr.Foreground.GetAnsiColorCode ()));
+                output.Append (EscSeqUtils.CSI_SetBackgroundColor (attr.Background.GetAnsiColorCode ()));
+
+                EscSeqUtils.CSI_AppendTextStyleChange (output, redrawTextStyle, attr.Style);
+            }
+            else
+            {
+                Write (output);
+                Console.ForegroundColor = (ConsoleColor)attr.Foreground.GetClosestNamedColor16 ();
+                Console.BackgroundColor = (ConsoleColor)attr.Background.GetClosestNamedColor16 ();
+            }
         }
         }
         else
         else
         {
         {
@@ -106,9 +117,9 @@ public class FakeOutput : OutputBase, IOutput
                                                       attr.Background.G,
                                                       attr.Background.G,
                                                       attr.Background.B
                                                       attr.Background.B
                                                      );
                                                      );
-        }
 
 
-        EscSeqUtils.CSI_AppendTextStyleChange (output, redrawTextStyle, attr.Style);
+            EscSeqUtils.CSI_AppendTextStyleChange (output, redrawTextStyle, attr.Style);
+        }
     }
     }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>

+ 164 - 123
Terminal.Gui/Drivers/IDriver.cs

@@ -1,3 +1,4 @@
+using System.Collections.Concurrent;
 
 
 namespace Terminal.Gui.Drivers;
 namespace Terminal.Gui.Drivers;
 
 
@@ -5,92 +6,107 @@ namespace Terminal.Gui.Drivers;
 /// <remarks>
 /// <remarks>
 ///     There are currently four implementations: UnixDriver, WindowsDriver, DotNetDriver, and FakeDriver
 ///     There are currently four implementations: UnixDriver, WindowsDriver, DotNetDriver, and FakeDriver
 /// </remarks>
 /// </remarks>
-public interface IDriver
+public interface IDriver : IDisposable
 {
 {
+    #region Driver Lifecycle
+
+    /// <summary>Initializes the driver</summary>
+    void Init ();
+
     /// <summary>
     /// <summary>
-    ///     Gets the name of the driver implementation.
+    ///     INTERNAL: Updates the terminal with the current output buffer. Should not be used by applications. Drawing occurs
+    ///     once each Application main loop iteration.
     /// </summary>
     /// </summary>
-    string? GetName ();
+    void Refresh ();
 
 
     /// <summary>
     /// <summary>
-    ///     Class responsible for processing native driver input objects
-    ///     e.g. <see cref="ConsoleKeyInfo"/> into <see cref="Key"/> events
-    ///     and detecting and processing ansi escape sequences.
+    ///     Gets the name of the driver implementation.
     /// </summary>
     /// </summary>
-    IInputProcessor InputProcessor { get; }
+    string? GetName ();
+
+    /// <summary>Returns the name of the driver and relevant library version information.</summary>
+    /// <returns></returns>
+    string GetVersionInfo ();
+
+    /// <summary>Suspends the application (e.g. on Linux via SIGTSTP) and upon resume, resets the console driver.</summary>
+    /// <remarks>This is only implemented in UnixDriver.</remarks>
+    void Suspend ();
 
 
     /// <summary>
     /// <summary>
-    ///     Describes the desired screen state. Data source for <see cref="IOutput"/>.
+    ///     Gets whether the driver has detected the console requires legacy console API (Windows Console API without ANSI/VT
+    ///     support).
+    ///     Returns <see langword="true"/> for legacy consoles that don't support modern ANSI escape sequences (e.g. Windows
+    ///     conhost);
+    ///     <see langword="false"/> for modern terminals with ANSI/VT support.
     /// </summary>
     /// </summary>
-    IOutputBuffer OutputBuffer { get; }
+    /// <remarks>
+    ///     <para>
+    ///         This property indicates whether the terminal supports modern ANSI escape sequences for input/output.
+    ///         On Windows, this maps to whether Virtual Terminal processing is enabled.
+    ///         On Unix-like systems, this is typically <see langword="false"/> as they support ANSI by default.
+    ///     </para>
+    ///     <para>
+    ///         When <see langword="true"/>, the driver must use legacy Windows Console API functions
+    ///         (e.g., WriteConsoleW, SetConsoleTextAttribute) instead of ANSI escape sequences.
+    ///     </para>
+    /// </remarks>
+    bool IsLegacyConsole { get; internal set; }
+
+    #endregion Driver Lifecycle
+
+    #region Driver Components
 
 
     /// <summary>
     /// <summary>
-    ///     Interface for classes responsible for reporting the current
-    ///     size of the terminal window.
+    ///     Class responsible for processing native driver input objects
+    ///     e.g. <see cref="ConsoleKeyInfo"/> into <see cref="Key"/> events
+    ///     and detecting and processing ansi escape sequences.
     /// </summary>
     /// </summary>
-    ISizeMonitor SizeMonitor { get; }
+    IInputProcessor InputProcessor { get; }
 
 
     /// <summary>Get the operating system clipboard.</summary>
     /// <summary>Get the operating system clipboard.</summary>
-    /// 
     IClipboard? Clipboard { get; }
     IClipboard? Clipboard { get; }
 
 
+    #endregion Driver Components
+
+    #region Screen and Display
+
     /// <summary>Gets the location and size of the terminal screen.</summary>
     /// <summary>Gets the location and size of the terminal screen.</summary>
     Rectangle Screen { get; }
     Rectangle Screen { get; }
 
 
     /// <summary>
     /// <summary>
-    /// Sets the screen size for testing purposes. Only supported by FakeDriver.
-    /// <see cref="Screen"/> is the source of truth for screen dimensions.
+    ///     Sets the screen size. <see cref="Screen"/> is the source of truth for screen dimensions.
     /// </summary>
     /// </summary>
     /// <param name="width">The new width in columns.</param>
     /// <param name="width">The new width in columns.</param>
     /// <param name="height">The new height in rows.</param>
     /// <param name="height">The new height in rows.</param>
-    /// <exception cref="NotSupportedException">Thrown when called on non-FakeDriver instances.</exception>
     void SetScreenSize (int width, int height);
     void SetScreenSize (int width, int height);
 
 
     /// <summary>
     /// <summary>
-    ///     Gets or sets the clip rectangle that <see cref="AddRune(Rune)"/> and <see cref="AddStr(string)"/> are subject
-    ///     to.
-    /// </summary>
-    /// <value>The rectangle describing the of <see cref="Clip"/> region.</value>
-    Region? Clip { get; set; }
-
-
-    /// <summary>
-    ///     Gets the column last set by <see cref="Move"/>. <see cref="Col"/> and <see cref="Row"/> are used by
-    ///     <see cref="AddRune(Rune)"/> and <see cref="AddStr"/> to determine where to add content.
+    ///     The event fired when the screen changes (size, position, etc.).
+    ///     <see cref="Screen"/> is the source of truth for screen dimensions.
     /// </summary>
     /// </summary>
-    int Col { get; }
+    event EventHandler<SizeChangedEventArgs>? SizeChanged;
 
 
     /// <summary>The number of columns visible in the terminal.</summary>
     /// <summary>The number of columns visible in the terminal.</summary>
     int Cols { get; set; }
     int Cols { get; set; }
 
 
-    // BUGBUG: This should not be publicly settable.
-    /// <summary>
-    ///     Gets or sets the contents of the application output. The driver outputs this buffer to the terminal.
-    ///     <remarks>The format of the array is rows, columns. The first index is the row, the second index is the column.</remarks>
-    /// </summary>
-    Cell [,]? Contents { get; set; }
+    /// <summary>The number of rows visible in the terminal.</summary>
+    int Rows { get; set; }
 
 
     /// <summary>The leftmost column in the terminal.</summary>
     /// <summary>The leftmost column in the terminal.</summary>
     int Left { get; set; }
     int Left { get; set; }
 
 
-    /// <summary>
-    ///     Gets the row last set by <see cref="Move"/>. <see cref="Col"/> and <see cref="Row"/> are used by
-    ///     <see cref="AddRune(Rune)"/> and <see cref="AddStr"/> to determine where to add content.
-    /// </summary>
-    int Row { get; }
-
-    /// <summary>The number of rows visible in the terminal.</summary>
-    int Rows { get; set; }
-
     /// <summary>The topmost row in the terminal.</summary>
     /// <summary>The topmost row in the terminal.</summary>
     int Top { get; set; }
     int Top { get; set; }
 
 
+    #endregion Screen and Display
+
+    #region Color Support
+
     /// <summary>Gets whether the <see cref="IDriver"/> supports TrueColor output.</summary>
     /// <summary>Gets whether the <see cref="IDriver"/> supports TrueColor output.</summary>
     bool SupportsTrueColor { get; }
     bool SupportsTrueColor { get; }
 
 
     /// <summary>
     /// <summary>
     ///     Gets or sets whether the <see cref="IDriver"/> should use 16 colors instead of the default TrueColors.
     ///     Gets or sets whether the <see cref="IDriver"/> should use 16 colors instead of the default TrueColors.
-    ///     See <see cref="Application.Force16Colors"/> to change this setting via <see cref="ConfigurationManager"/>.
     /// </summary>
     /// </summary>
     /// <remarks>
     /// <remarks>
     ///     <para>
     ///     <para>
@@ -98,42 +114,57 @@ public interface IDriver
     ///         <see langword="false"/>, indicating that the <see cref="IDriver"/> cannot support TrueColor.
     ///         <see langword="false"/>, indicating that the <see cref="IDriver"/> cannot support TrueColor.
     ///     </para>
     ///     </para>
     /// </remarks>
     /// </remarks>
+    /// <seealso cref="Driver.Force16Colors"/>
     bool Force16Colors { get; set; }
     bool Force16Colors { get; set; }
 
 
+    #endregion Color Support
+
+    #region Content Buffer
+
+    // BUGBUG: This should not be publicly settable.
     /// <summary>
     /// <summary>
-    ///     The <see cref="System.Attribute"/> that will be used for the next <see cref="AddRune(Rune)"/> or <see cref="AddStr"/>
-    ///     call.
+    ///     Gets or sets the contents of the application output. The driver outputs this buffer to the terminal.
+    ///     <remarks>The format of the array is rows, columns. The first index is the row, the second index is the column.</remarks>
     /// </summary>
     /// </summary>
-    Attribute CurrentAttribute { get; set; }
+    Cell [,]? Contents { get; set; }
 
 
-    /// <summary>Returns the name of the driver and relevant library version information.</summary>
-    /// <returns></returns>
-    string GetVersionInfo ();
+    /// <summary>
+    ///     Gets or sets the clip rectangle that <see cref="AddRune(Rune)"/> and <see cref="AddStr(string)"/> are subject
+    ///     to.
+    /// </summary>
+    /// <value>The rectangle describing the of <see cref="Clip"/> region.</value>
+    Region? Clip { get; set; }
+
+    /// <summary>Clears the <see cref="IDriver.Contents"/> of the driver.</summary>
+    void ClearContents ();
 
 
     /// <summary>
     /// <summary>
-    ///     Provide proper writing to send escape sequence recognized by the <see cref="IDriver"/>.
+    ///     Fills the specified rectangle with the specified rune, using <see cref="IDriver.CurrentAttribute"/>
     /// </summary>
     /// </summary>
-    /// <param name="ansi"></param>
-    void WriteRaw (string ansi);
+    event EventHandler<EventArgs> ClearedContents;
 
 
-    /// <summary>Tests if the specified rune is supported by the driver.</summary>
-    /// <param name="rune"></param>
-    /// <returns>
-    ///     <see langword="true"/> if the rune can be properly presented; <see langword="false"/> if the driver does not
-    ///     support displaying this rune.
-    /// </returns>
-    bool IsRuneSupported (Rune rune);
+    #endregion Content Buffer
 
 
-    /// <summary>Tests whether the specified coordinate are valid for drawing the specified Text.</summary>
-    /// <param name="text">Used to determine if one or two columns are required.</param>
-    /// <param name="col">The column.</param>
-    /// <param name="row">The row.</param>
-    /// <returns>
-    ///     <see langword="false"/> if the coordinate is outside the screen bounds or outside of
-    ///     <see cref="IDriver.Clip"/>.
-    ///     <see langword="true"/> otherwise.
-    /// </returns>
-    bool IsValidLocation (string text, int col, int row);
+    #region Drawing and Rendering
+
+    /// <summary>
+    ///     Gets the column last set by <see cref="Move"/>. <see cref="Col"/> and <see cref="Row"/> are used by
+    ///     <see cref="AddRune(Rune)"/> and <see cref="AddStr"/> to determine where to add content.
+    /// </summary>
+    int Col { get; }
+
+    /// <summary>
+    ///     Gets the row last set by <see cref="Move"/>. <see cref="Col"/> and <see cref="Row"/> are used by
+    ///     <see cref="AddRune(Rune)"/> and <see cref="AddStr"/> to determine where to add content.
+    /// </summary>
+    int Row { get; }
+
+    /// <summary>
+    ///     The <see cref="System.Attribute"/> that will be used for the next <see cref="AddRune(Rune)"/> or
+    ///     <see cref="AddStr"/>
+    ///     call.
+    /// </summary>
+    Attribute CurrentAttribute { get; set; }
 
 
     /// <summary>
     /// <summary>
     ///     Updates <see cref="IDriver.Col"/> and <see cref="IDriver.Row"/> to the specified column and row in
     ///     Updates <see cref="IDriver.Col"/> and <see cref="IDriver.Row"/> to the specified column and row in
@@ -153,6 +184,25 @@ public interface IDriver
     /// <param name="row">Row to move to.</param>
     /// <param name="row">Row to move to.</param>
     void Move (int col, int row);
     void Move (int col, int row);
 
 
+    /// <summary>Tests if the specified rune is supported by the driver.</summary>
+    /// <param name="rune"></param>
+    /// <returns>
+    ///     <see langword="true"/> if the rune can be properly presented; <see langword="false"/> if the driver does not
+    ///     support displaying this rune.
+    /// </returns>
+    bool IsRuneSupported (Rune rune);
+
+    /// <summary>Tests whether the specified coordinate are valid for drawing the specified Text.</summary>
+    /// <param name="text">Used to determine if one or two columns are required.</param>
+    /// <param name="col">The column.</param>
+    /// <param name="row">The row.</param>
+    /// <returns>
+    ///     <see langword="false"/> if the coordinate is outside the screen bounds or outside of
+    ///     <see cref="IDriver.Clip"/>.
+    ///     <see langword="true"/> otherwise.
+    /// </returns>
+    bool IsValidLocation (string text, int col, int row);
+
     /// <summary>Adds the specified rune to the display at the current cursor position.</summary>
     /// <summary>Adds the specified rune to the display at the current cursor position.</summary>
     /// <remarks>
     /// <remarks>
     ///     <para>
     ///     <para>
@@ -193,14 +243,6 @@ public interface IDriver
     /// <param name="str">String.</param>
     /// <param name="str">String.</param>
     void AddStr (string str);
     void AddStr (string str);
 
 
-    /// <summary>Clears the <see cref="IDriver.Contents"/> of the driver.</summary>
-    void ClearContents ();
-
-    /// <summary>
-    ///     Fills the specified rectangle with the specified rune, using <see cref="IDriver.CurrentAttribute"/>
-    /// </summary>
-    event EventHandler<EventArgs> ClearedContents;
-
     /// <summary>Fills the specified rectangle with the specified rune, using <see cref="IDriver.CurrentAttribute"/></summary>
     /// <summary>Fills the specified rectangle with the specified rune, using <see cref="IDriver.CurrentAttribute"/></summary>
     /// <remarks>
     /// <remarks>
     ///     The value of <see cref="IDriver.Clip"/> is honored. Any parts of the rectangle not in the clip will not be
     ///     The value of <see cref="IDriver.Clip"/> is honored. Any parts of the rectangle not in the clip will not be
@@ -218,29 +260,43 @@ public interface IDriver
     /// <param name="c"></param>
     /// <param name="c"></param>
     void FillRect (Rectangle rect, char c);
     void FillRect (Rectangle rect, char c);
 
 
+    /// <summary>Selects the specified attribute as the attribute to use for future calls to AddRune and AddString.</summary>
+    /// <remarks>Implementations should call <c>base.SetAttribute(c)</c>.</remarks>
+    /// <param name="c">C.</param>
+    Attribute SetAttribute (Attribute c);
 
 
-    /// <summary>Gets the terminal cursor visibility.</summary>
-    /// <param name="visibility">The current <see cref="CursorVisibility"/></param>
-    /// <returns><see langword="true"/> upon success</returns>
-    bool GetCursorVisibility (out CursorVisibility visibility);
+    /// <summary>Gets the current <see cref="Attribute"/>.</summary>
+    /// <returns>The current attribute.</returns>
+    Attribute GetAttribute ();
 
 
-    /// <summary>Updates the screen to reflect all the changes that have been done to the display buffer</summary>
-    void Refresh ();
+    /// <summary>
+    ///     Provide proper writing to send escape sequence recognized by the <see cref="IDriver"/>.
+    /// </summary>
+    /// <param name="ansi"></param>
+    void WriteRaw (string ansi);
 
 
-    /// <summary>Sets the terminal cursor visibility.</summary>
-    /// <param name="visibility">The wished <see cref="CursorVisibility"/></param>
-    /// <returns><see langword="true"/> upon success</returns>
-    bool SetCursorVisibility (CursorVisibility visibility);
+    /// <summary>
+    ///     Gets the queue of sixel images to write out to screen when updating.
+    ///     If the terminal does not support Sixel, adding to this queue has no effect.
+    /// </summary>
+    ConcurrentQueue<SixelToRender> GetSixels ();
 
 
     /// <summary>
     /// <summary>
-    /// The event fired when the screen changes (size, position, etc.).
-    /// <see cref="Screen"/> is the source of truth for screen dimensions.
+    ///     Gets a string representation of <see cref="Contents"/>.
     /// </summary>
     /// </summary>
-    event EventHandler<SizeChangedEventArgs>? SizeChanged;
+    /// <returns></returns>
+    public string ToString ();
 
 
-    /// <summary>Suspends the application (e.g. on Linux via SIGTSTP) and upon resume, resets the console driver.</summary>
-    /// <remarks>This is only implemented in UnixDriver.</remarks>
-    void Suspend ();
+    /// <summary>
+    ///     Gets an ANSI escape sequence representation of <see cref="Contents"/>. This is the
+    ///     same output as would be written to the terminal to recreate the current screen contents.
+    /// </summary>
+    /// <returns></returns>
+    public string ToAnsi ();
+
+    #endregion Drawing and Rendering
+
+    #region Cursor
 
 
     /// <summary>
     /// <summary>
     ///     Sets the position of the terminal cursor to <see cref="IDriver.Col"/> and
     ///     Sets the position of the terminal cursor to <see cref="IDriver.Col"/> and
@@ -248,20 +304,19 @@ public interface IDriver
     /// </summary>
     /// </summary>
     void UpdateCursor ();
     void UpdateCursor ();
 
 
-    /// <summary>Initializes the driver</summary>
-    void Init ();
+    /// <summary>Gets the terminal cursor visibility.</summary>
+    /// <param name="visibility">The current <see cref="CursorVisibility"/></param>
+    /// <returns><see langword="true"/> upon success</returns>
+    bool GetCursorVisibility (out CursorVisibility visibility);
 
 
-    /// <summary>Ends the execution of the console driver.</summary>
-    void End ();
+    /// <summary>Sets the terminal cursor visibility.</summary>
+    /// <param name="visibility">The wished <see cref="CursorVisibility"/></param>
+    /// <returns><see langword="true"/> upon success</returns>
+    bool SetCursorVisibility (CursorVisibility visibility);
 
 
-    /// <summary>Selects the specified attribute as the attribute to use for future calls to AddRune and AddString.</summary>
-    /// <remarks>Implementations should call <c>base.SetAttribute(c)</c>.</remarks>
-    /// <param name="c">C.</param>
-    Attribute SetAttribute (Attribute c);
+    #endregion Cursor
 
 
-    /// <summary>Gets the current <see cref="Attribute"/>.</summary>
-    /// <returns>The current attribute.</returns>
-    Attribute GetAttribute ();
+    #region Input Events
 
 
     /// <summary>Event fired when a mouse event occurs.</summary>
     /// <summary>Event fired when a mouse event occurs.</summary>
     event EventHandler<MouseEventArgs>? MouseEvent;
     event EventHandler<MouseEventArgs>? MouseEvent;
@@ -283,29 +338,15 @@ public interface IDriver
     /// <param name="key"></param>
     /// <param name="key"></param>
     void EnqueueKeyEvent (Key key);
     void EnqueueKeyEvent (Key key);
 
 
+    #endregion Input Events
+
+    #region ANSI Escape Sequences
+
     /// <summary>
     /// <summary>
     ///     Queues the given <paramref name="request"/> for execution
     ///     Queues the given <paramref name="request"/> for execution
     /// </summary>
     /// </summary>
     /// <param name="request"></param>
     /// <param name="request"></param>
     public void QueueAnsiRequest (AnsiEscapeSequenceRequest request);
     public void QueueAnsiRequest (AnsiEscapeSequenceRequest request);
 
 
-    /// <summary>
-    ///     Gets the <see cref="AnsiRequestScheduler"/> for the driver
-    /// </summary>
-    /// <returns></returns>
-    public AnsiRequestScheduler GetRequestScheduler ();
-
-
-    /// <summary>
-    ///     Gets a string representation of <see cref="Contents"/>.
-    /// </summary>
-    /// <returns></returns>
-    public string ToString ();
-
-    /// <summary>
-    ///     Gets an ANSI escape sequence representation of <see cref="Contents"/>. This is the
-    ///     same output as would be written to the terminal to recreate the current screen contents.
-    /// </summary>
-    /// <returns></returns>
-    public string ToAnsi ();
+    #endregion ANSI Escape Sequences
 }
 }

+ 13 - 2
Terminal.Gui/Drivers/IOutput.cs

@@ -1,4 +1,6 @@
-namespace Terminal.Gui.Drivers;
+using System.Collections.Concurrent;
+
+namespace Terminal.Gui.Drivers;
 
 
 /// <summary>
 /// <summary>
 ///     The low-level interface drivers implement to provide output capabilities; encapsulates platform-specific
 ///     The low-level interface drivers implement to provide output capabilities; encapsulates platform-specific
@@ -6,6 +8,15 @@
 /// </summary>
 /// </summary>
 public interface IOutput : IDisposable
 public interface IOutput : IDisposable
 {
 {
+    /// <seealso cref="IDriver.Force16Colors"/>
+    bool Force16Colors { get; set; }
+
+    /// <seealso cref="IDriver.IsLegacyConsole"/>
+    bool IsLegacyConsole { get; set; }
+
+    /// <seealso cref="IDriver.GetSixels"/>
+    ConcurrentQueue<SixelToRender> GetSixels ();
+
     /// <summary>
     /// <summary>
     ///     Gets the current position of the console cursor.
     ///     Gets the current position of the console cursor.
     /// </summary>
     /// </summary>
@@ -17,7 +28,7 @@ public interface IOutput : IDisposable
     ///     of characters not pixels).
     ///     of characters not pixels).
     /// </summary>
     /// </summary>
     /// <returns></returns>
     /// <returns></returns>
-    public Size GetSize ();
+    Size GetSize ();
 
 
     /// <summary>
     /// <summary>
     ///     Moves the console cursor to the given location.
     ///     Moves the console cursor to the given location.

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

@@ -14,6 +14,6 @@ public interface ISizeMonitor
     ///     Examines the current size of the terminal and raises <see cref="SizeChanged"/> if it is different
     ///     Examines the current size of the terminal and raises <see cref="SizeChanged"/> if it is different
     ///     from last inspection.
     ///     from last inspection.
     /// </summary>
     /// </summary>
-    /// <returns></returns>
+    /// <returns><see langword="true"/> if the size has changed; otherwise, <see langword="false"/>.</returns>
     bool Poll ();
     bool Poll ();
 }
 }

+ 84 - 25
Terminal.Gui/Drivers/OutputBase.cs

@@ -1,3 +1,5 @@
+using System.Collections.Concurrent;
+
 namespace Terminal.Gui.Drivers;
 namespace Terminal.Gui.Drivers;
 
 
 /// <summary>
 /// <summary>
@@ -5,7 +7,44 @@ namespace Terminal.Gui.Drivers;
 /// </summary>
 /// </summary>
 public abstract class OutputBase
 public abstract class OutputBase
 {
 {
-    private CursorVisibility? _cachedCursorVisibility;
+    private bool _force16Colors;
+
+    /// <inheritdoc cref="IOutput.Force16Colors"/>
+    public bool Force16Colors
+    {
+        get => _force16Colors;
+        set
+        {
+            if (IsLegacyConsole && !value)
+            {
+                return;
+            }
+
+            _force16Colors = value;
+        }
+    }
+
+    private bool _isLegacyConsole;
+
+    /// <inheritdoc cref="IOutput.IsLegacyConsole"/>
+    public bool IsLegacyConsole
+    {
+        get => _isLegacyConsole;
+        set
+        {
+            _isLegacyConsole = value;
+
+            if (value) // If legacy console (true), force 16 colors
+            {
+                Force16Colors = true;
+            }
+        }
+    }
+
+    private readonly ConcurrentQueue<SixelToRender> _sixels = [];
+
+    /// <inheritdoc cref="IOutput.GetSixels"/>>
+    public ConcurrentQueue<SixelToRender> GetSixels () => _sixels;
 
 
     // Last text style used, for updating style with EscSeqUtils.CSI_AppendTextStyleChange().
     // Last text style used, for updating style with EscSeqUtils.CSI_AppendTextStyleChange().
     private TextStyle _redrawTextStyle = TextStyle.None;
     private TextStyle _redrawTextStyle = TextStyle.None;
@@ -28,7 +67,6 @@ public abstract class OutputBase
         Attribute? redrawAttr = null;
         Attribute? redrawAttr = null;
         int lastCol = -1;
         int lastCol = -1;
 
 
-        CursorVisibility? savedVisibility = _cachedCursorVisibility;
         SetCursorVisibility (CursorVisibility.Invisible);
         SetCursorVisibility (CursorVisibility.Invisible);
 
 
         for (int row = top; row < rows; row++)
         for (int row = top; row < rows; row++)
@@ -63,6 +101,8 @@ public abstract class OutputBase
                             lastCol++;
                             lastCol++;
                         }
                         }
 
 
+                        SetCursorPositionImpl (lastCol, row);
+
                         continue;
                         continue;
                     }
                     }
 
 
@@ -82,27 +122,41 @@ public abstract class OutputBase
 
 
             if (output.Length > 0)
             if (output.Length > 0)
             {
             {
-                SetCursorPositionImpl (lastCol, row);
+                if (IsLegacyConsole)
+                {
+                    Write (output);
+                }
+                else
+                {
+                    SetCursorPositionImpl (lastCol, row);
+
+                    // Wrap URLs with OSC 8 hyperlink sequences using the new Osc8UrlLinker
+                    StringBuilder processed = Osc8UrlLinker.WrapOsc8 (output);
+                    Write (processed);
+                }
+            }
+        }
+
+        if (IsLegacyConsole)
+        {
+            return;
+        }
 
 
-                // Wrap URLs with OSC 8 hyperlink sequences using the new Osc8UrlLinker
-                StringBuilder processed = Osc8UrlLinker.WrapOsc8 (output);
-                Write (processed);
+        foreach (SixelToRender s in GetSixels ())
+        {
+            if (string.IsNullOrWhiteSpace (s.SixelData))
+            {
+                continue;
             }
             }
+
+            SetCursorPositionImpl (s.ScreenPosition.X, s.ScreenPosition.Y);
+            Write ((StringBuilder)new (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;
+
+        // DO NOT restore cursor visibility here - let ApplicationMainLoop.SetCursor() handle it
+        // The old code was saving/restoring visibility which caused flickering because
+        // it would restore to the old value even if the application wanted it hidden
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -166,7 +220,7 @@ public abstract class OutputBase
                     continue;
                     continue;
                 }
                 }
 
 
-                Cell cell = buffer.Contents![row, col];
+                Cell cell = buffer.Contents! [row, col];
                 AppendCellAnsi (cell, output, ref lastAttr, ref redrawTextStyle, endCol, ref col);
                 AppendCellAnsi (cell, output, ref lastAttr, ref redrawTextStyle, endCol, ref col);
             }
             }
 
 
@@ -228,11 +282,16 @@ public abstract class OutputBase
 
 
     private void WriteToConsole (StringBuilder output, ref int lastCol, int row, ref int outputWidth)
     private void WriteToConsole (StringBuilder output, ref int lastCol, int row, ref int outputWidth)
     {
     {
-        SetCursorPositionImpl (lastCol, row);
-
-        // Wrap URLs with OSC 8 hyperlink sequences using the new Osc8UrlLinker
-        StringBuilder processed = Osc8UrlLinker.WrapOsc8 (output);
-        Write (processed);
+        if (IsLegacyConsole)
+        {
+            Write (output);
+        }
+        else
+        {
+            // Wrap URLs with OSC 8 hyperlink sequences using the new Osc8UrlLinker
+            StringBuilder processed = Osc8UrlLinker.WrapOsc8 (output);
+            Write (processed);
+        }
 
 
         output.Clear ();
         output.Clear ();
         lastCol += outputWidth;
         lastCol += outputWidth;

+ 1 - 1
Terminal.Gui/Drivers/UnixDriver/UnixOutput.cs

@@ -39,7 +39,7 @@ internal class UnixOutput : OutputBase, IOutput
     /// <inheritdoc />
     /// <inheritdoc />
     protected override void AppendOrWriteAttribute (StringBuilder output, Attribute attr, TextStyle redrawTextStyle)
     protected override void AppendOrWriteAttribute (StringBuilder output, Attribute attr, TextStyle redrawTextStyle)
     {
     {
-        if (Application.Force16Colors)
+        if (Force16Colors)
         {
         {
             output.Append (EscSeqUtils.CSI_SetForegroundColor (attr.Foreground.GetAnsiColorCode ()));
             output.Append (EscSeqUtils.CSI_SetForegroundColor (attr.Foreground.GetAnsiColorCode ()));
             output.Append (EscSeqUtils.CSI_SetBackgroundColor (attr.Background.GetAnsiColorCode ()));
             output.Append (EscSeqUtils.CSI_SetBackgroundColor (attr.Background.GetAnsiColorCode ()));

+ 75 - 99
Terminal.Gui/Drivers/WindowsDriver/WindowsOutput.cs

@@ -97,13 +97,12 @@ internal partial class WindowsOutput : OutputBase, IOutput
     private const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004;
     private const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004;
     private readonly nint _outputHandle;
     private readonly nint _outputHandle;
     private nint _screenBuffer;
     private nint _screenBuffer;
-    private readonly bool _isVirtualTerminal;
     private readonly ConsoleColor _foreground;
     private readonly ConsoleColor _foreground;
     private readonly ConsoleColor _background;
     private readonly ConsoleColor _background;
 
 
     public WindowsOutput ()
     public WindowsOutput ()
     {
     {
-        Logging.Logger.LogInformation ($"Creating {nameof (WindowsOutput)}");
+        Logging.Information ($"Creating {nameof (WindowsOutput)}");
 
 
         if (!RuntimeInformation.IsOSPlatform (OSPlatform.Windows))
         if (!RuntimeInformation.IsOSPlatform (OSPlatform.Windows))
         {
         {
@@ -113,22 +112,9 @@ internal partial class WindowsOutput : OutputBase, IOutput
         // Get the standard output handle which is the current screen buffer.
         // Get the standard output handle which is the current screen buffer.
         _outputHandle = GetStdHandle (STD_OUTPUT_HANDLE);
         _outputHandle = GetStdHandle (STD_OUTPUT_HANDLE);
         GetConsoleMode (_outputHandle, out uint mode);
         GetConsoleMode (_outputHandle, out uint mode);
-        _isVirtualTerminal = (mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0;
+        IsLegacyConsole = (mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0;
 
 
-        if (_isVirtualTerminal)
-        {
-            if (Environment.GetEnvironmentVariable ("VSAPPIDNAME") is null)
-            {
-                //Enable alternative screen buffer.
-                Console.Out.Write (EscSeqUtils.CSI_SaveCursorAndActivateAltBufferNoBackscroll);
-            }
-            else
-            {
-                _foreground = Console.ForegroundColor;
-                _background = Console.BackgroundColor;
-            }
-        }
-        else
+        if (IsLegacyConsole)
         {
         {
             CreateScreenBuffer ();
             CreateScreenBuffer ();
 
 
@@ -145,12 +131,19 @@ internal partial class WindowsOutput : OutputBase, IOutput
             {
             {
                 throw new ApplicationException ($"Failed to set screenBuffer console mode, error code: {Marshal.GetLastWin32Error ()}.");
                 throw new ApplicationException ($"Failed to set screenBuffer console mode, error code: {Marshal.GetLastWin32Error ()}.");
             }
             }
-
-            // 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;
-
+        }
+        else
+        {
+            if (Environment.GetEnvironmentVariable ("VSAPPIDNAME") is null)
+            {
+                //Enable alternative screen buffer.
+                Console.Out.Write (EscSeqUtils.CSI_SaveCursorAndActivateAltBufferNoBackscroll);
+            }
+            else
+            {
+                _foreground = Console.ForegroundColor;
+                _background = Console.BackgroundColor;
+            }
         }
         }
 
 
         GetSize ();
         GetSize ();
@@ -189,7 +182,7 @@ internal partial class WindowsOutput : OutputBase, IOutput
             return;
             return;
         }
         }
 
 
-        if (!WriteConsole (_isVirtualTerminal ? _outputHandle : _screenBuffer, str, (uint)str.Length, out uint _, nint.Zero))
+        if (!WriteConsole (!IsLegacyConsole ? _outputHandle : _screenBuffer, str, (uint)str.Length, out uint _, nint.Zero))
         {
         {
             throw new Win32Exception (Marshal.GetLastWin32Error (), "Failed to write to console screen buffer.");
             throw new Win32Exception (Marshal.GetLastWin32Error (), "Failed to write to console screen buffer.");
         }
         }
@@ -220,19 +213,19 @@ internal partial class WindowsOutput : OutputBase, IOutput
         var csbi = new WindowsConsole.CONSOLE_SCREEN_BUFFER_INFOEX ();
         var csbi = new WindowsConsole.CONSOLE_SCREEN_BUFFER_INFOEX ();
         csbi.cbSize = (uint)Marshal.SizeOf (csbi);
         csbi.cbSize = (uint)Marshal.SizeOf (csbi);
 
 
-        if (!GetConsoleScreenBufferInfoEx (_isVirtualTerminal ? _outputHandle : _screenBuffer, ref csbi))
+        if (!GetConsoleScreenBufferInfoEx (!IsLegacyConsole ? _outputHandle : _screenBuffer, ref csbi))
         {
         {
             throw new Win32Exception (Marshal.GetLastWin32Error ());
             throw new Win32Exception (Marshal.GetLastWin32Error ());
         }
         }
 
 
-        WindowsConsole.Coord maxWinSize = GetLargestConsoleWindowSize (_isVirtualTerminal ? _outputHandle : _screenBuffer);
+        WindowsConsole.Coord maxWinSize = GetLargestConsoleWindowSize (!IsLegacyConsole ? _outputHandle : _screenBuffer);
         short newCols = Math.Min (cols, maxWinSize.X);
         short newCols = Math.Min (cols, maxWinSize.X);
         short newRows = Math.Min (rows, maxWinSize.Y);
         short newRows = Math.Min (rows, maxWinSize.Y);
         csbi.dwSize = new (newCols, Math.Max (newRows, (short)1));
         csbi.dwSize = new (newCols, Math.Max (newRows, (short)1));
         csbi.srWindow = new (0, 0, newCols, newRows);
         csbi.srWindow = new (0, 0, newCols, newRows);
         csbi.dwMaximumWindowSize = new (newCols, newRows);
         csbi.dwMaximumWindowSize = new (newCols, newRows);
 
 
-        if (!SetConsoleScreenBufferInfoEx (_isVirtualTerminal ? _outputHandle : _screenBuffer, ref csbi))
+        if (!SetConsoleScreenBufferInfoEx (!IsLegacyConsole ? _outputHandle : _screenBuffer, ref csbi))
         {
         {
             throw new Win32Exception (Marshal.GetLastWin32Error ());
             throw new Win32Exception (Marshal.GetLastWin32Error ());
         }
         }
@@ -252,11 +245,11 @@ internal partial class WindowsOutput : OutputBase, IOutput
 
 
     private void SetConsoleOutputWindow (WindowsConsole.CONSOLE_SCREEN_BUFFER_INFOEX csbi)
     private void SetConsoleOutputWindow (WindowsConsole.CONSOLE_SCREEN_BUFFER_INFOEX csbi)
     {
     {
-        if ((_isVirtualTerminal
+        if ((!IsLegacyConsole
                  ? _outputHandle
                  ? _outputHandle
                  : _screenBuffer)
                  : _screenBuffer)
             != nint.Zero
             != nint.Zero
-            && !SetConsoleScreenBufferInfoEx (_isVirtualTerminal ? _outputHandle : _screenBuffer, ref csbi))
+            && !SetConsoleScreenBufferInfoEx (!IsLegacyConsole ? _outputHandle : _screenBuffer, ref csbi))
         {
         {
             throw new Win32Exception (Marshal.GetLastWin32Error ());
             throw new Win32Exception (Marshal.GetLastWin32Error ());
         }
         }
@@ -264,65 +257,52 @@ internal partial class WindowsOutput : OutputBase, IOutput
 
 
     public override void Write (IOutputBuffer outputBuffer)
     public override void Write (IOutputBuffer outputBuffer)
     {
     {
-        // BUGBUG: This is bad. It does not work if the app was crated without
-        // BUGBUG: Apis.
-        //_force16Colors = ApplicationImpl.Instance.Driver!.Force16Colors;
-        _force16Colors = false;
         _everythingStringBuilder.Clear ();
         _everythingStringBuilder.Clear ();
 
 
-        // for 16 color mode we will write to a backing buffer then flip it to the active one at the end to avoid jitter.
+        // for 16 color mode we will write to a backing buffer, then flip it to the active one at the end to avoid jitter.
         _consoleBuffer = 0;
         _consoleBuffer = 0;
 
 
-        if (_force16Colors)
+        if (Force16Colors)
         {
         {
-            if (_isVirtualTerminal)
-            {
-                _consoleBuffer = _outputHandle;
-            }
-            else
-            {
-                _consoleBuffer = _screenBuffer;
-            }
+            _consoleBuffer = !IsLegacyConsole ? _outputHandle : _screenBuffer;
         }
         }
         else
         else
         {
         {
             _consoleBuffer = _outputHandle;
             _consoleBuffer = _outputHandle;
         }
         }
 
 
-        base.Write (outputBuffer);
-
         try
         try
         {
         {
-            if (_force16Colors && !_isVirtualTerminal)
-            {
-                SetConsoleActiveScreenBuffer (_consoleBuffer);
-            }
-            else
+            base.Write (outputBuffer);
+
+            ReadOnlySpan<char> span = _everythingStringBuilder.ToString ().AsSpan (); // still allocates the string
+
+            bool result = WriteConsole (_consoleBuffer, span, (uint)span.Length, out _, nint.Zero);
+
+            if (!result)
             {
             {
-                ReadOnlySpan<char> span = _everythingStringBuilder.ToString ().AsSpan (); // still allocates the string
+                int err = Marshal.GetLastWin32Error ();
+
+                if (err == 1)
+                {
+                    Logging.Error ($"Error: {Marshal.GetLastWin32Error ()} in {nameof (WindowsOutput)}");
 
 
-                bool result = WriteConsole (_consoleBuffer, span, (uint)span.Length, out _, nint.Zero);
+                    return;
+                }
 
 
-                if (!result)
+                if (err != 0)
                 {
                 {
-                    int err = Marshal.GetLastWin32Error ();
-
-                    if (err == 1)
-                    {
-                        Logging.Logger.LogError ($"Error: {Marshal.GetLastWin32Error ()} in {nameof (WindowsOutput)}");
-
-                        return;
-                    }
-                    if (err != 0)
-                    {
-                        throw new Win32Exception (err);
-                    }
+                    throw new Win32Exception (err);
                 }
                 }
             }
             }
         }
         }
+        catch (DllNotFoundException)
+        {
+            // Running unit tests or in an environment where writing is not possible.
+        }
         catch (Exception e)
         catch (Exception e)
         {
         {
-            Logging.Logger.LogError ($"Error: {e.Message} in {nameof (WindowsOutput)}");
+            Logging.Error ($"Error: {e.Message} in {nameof (WindowsOutput)}");
 
 
             if (RuntimeInformation.IsOSPlatform (OSPlatform.Windows))
             if (RuntimeInformation.IsOSPlatform (OSPlatform.Windows))
             {
             {
@@ -341,7 +321,7 @@ internal partial class WindowsOutput : OutputBase, IOutput
 
 
         var str = output.ToString ();
         var str = output.ToString ();
 
 
-        if (_force16Colors && !_isVirtualTerminal)
+        if (Force16Colors && IsLegacyConsole)
         {
         {
             char [] a = str.ToCharArray ();
             char [] a = str.ToCharArray ();
             WriteConsole (_screenBuffer, a, (uint)a.Length, out _, nint.Zero);
             WriteConsole (_screenBuffer, a, (uint)a.Length, out _, nint.Zero);
@@ -355,23 +335,20 @@ internal partial class WindowsOutput : OutputBase, IOutput
     /// <inheritdoc/>
     /// <inheritdoc/>
     protected override void AppendOrWriteAttribute (StringBuilder output, Attribute attr, TextStyle redrawTextStyle)
     protected override void AppendOrWriteAttribute (StringBuilder output, Attribute attr, TextStyle redrawTextStyle)
     {
     {
-        // BUGBUG: This is bad. It does not work if the app was crated without
-        // BUGBUG: Apis.
-        // bool force16Colors = ApplicationImpl.Instance.Force16Colors;
-        bool force16Colors = false;
-
-        if (force16Colors)
+        if (Force16Colors)
         {
         {
-            if (_isVirtualTerminal)
+            if (IsLegacyConsole)
             {
             {
-                output.Append (EscSeqUtils.CSI_SetForegroundColor (attr.Foreground.GetAnsiColorCode ()));
-                output.Append (EscSeqUtils.CSI_SetBackgroundColor (attr.Background.GetAnsiColorCode ()));
-                EscSeqUtils.CSI_AppendTextStyleChange (output, redrawTextStyle, attr.Style);
+                Write (output);
+                output.Clear ();
+                var as16ColorInt = (ushort)((int)attr.Foreground.GetClosestNamedColor16 () | ((int)attr.Background.GetClosestNamedColor16 () << 4));
+                SetConsoleTextAttribute (_screenBuffer, as16ColorInt);
             }
             }
             else
             else
             {
             {
-                var as16ColorInt = (ushort)((int)attr.Foreground.GetClosestNamedColor16 () | ((int)attr.Background.GetClosestNamedColor16 () << 4));
-                SetConsoleTextAttribute (_screenBuffer, as16ColorInt);
+                output.Append (EscSeqUtils.CSI_SetForegroundColor (attr.Foreground.GetAnsiColorCode ()));
+                output.Append (EscSeqUtils.CSI_SetBackgroundColor (attr.Background.GetAnsiColorCode ()));
+                EscSeqUtils.CSI_AppendTextStyleChange (output, redrawTextStyle, attr.Style);
             }
             }
         }
         }
         else
         else
@@ -438,7 +415,7 @@ internal partial class WindowsOutput : OutputBase, IOutput
             var csbi = new WindowsConsole.CONSOLE_SCREEN_BUFFER_INFOEX ();
             var csbi = new WindowsConsole.CONSOLE_SCREEN_BUFFER_INFOEX ();
             csbi.cbSize = (uint)Marshal.SizeOf (csbi);
             csbi.cbSize = (uint)Marshal.SizeOf (csbi);
 
 
-            if (!GetConsoleScreenBufferInfoEx (_isVirtualTerminal ? _outputHandle : _screenBuffer, ref csbi))
+            if (!GetConsoleScreenBufferInfoEx (!IsLegacyConsole ? _outputHandle : _screenBuffer, ref csbi))
             {
             {
                 //throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
                 //throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
                 cursorPosition = default (WindowsConsole.Coord);
                 cursorPosition = default (WindowsConsole.Coord);
@@ -468,7 +445,7 @@ internal partial class WindowsOutput : OutputBase, IOutput
 
 
         try
         try
         {
         {
-            maxWinSize = GetLargestConsoleWindowSize (_isVirtualTerminal ? _outputHandle : _screenBuffer);
+            maxWinSize = GetLargestConsoleWindowSize (!IsLegacyConsole ? _outputHandle : _screenBuffer);
         }
         }
         catch
         catch
         {
         {
@@ -481,7 +458,7 @@ internal partial class WindowsOutput : OutputBase, IOutput
     /// <inheritdoc/>
     /// <inheritdoc/>
     protected override bool SetCursorPositionImpl (int screenPositionX, int screenPositionY)
     protected override bool SetCursorPositionImpl (int screenPositionX, int screenPositionY)
     {
     {
-        if (_force16Colors && !_isVirtualTerminal)
+        if (Force16Colors && IsLegacyConsole)
         {
         {
             SetConsoleCursorPosition (_screenBuffer, new ((short)screenPositionX, (short)screenPositionY));
             SetConsoleCursorPosition (_screenBuffer, new ((short)screenPositionX, (short)screenPositionY));
         }
         }
@@ -505,7 +482,7 @@ internal partial class WindowsOutput : OutputBase, IOutput
             return;
             return;
         }
         }
 
 
-        if (!_isVirtualTerminal)
+        if (IsLegacyConsole)
         {
         {
             var info = new WindowsConsole.ConsoleCursorInfo
             var info = new WindowsConsole.ConsoleCursorInfo
             {
             {
@@ -539,15 +516,15 @@ internal partial class WindowsOutput : OutputBase, IOutput
 
 
         _lastCursorPosition = new (col, row);
         _lastCursorPosition = new (col, row);
 
 
-        if (_isVirtualTerminal)
+        if (IsLegacyConsole)
         {
         {
-            var sb = new StringBuilder ();
-            EscSeqUtils.CSI_AppendCursorPosition (sb, row + 1, col + 1);
-            Write (sb.ToString ());
+            SetConsoleCursorPosition (_screenBuffer, new ((short)col, (short)row));
         }
         }
         else
         else
         {
         {
-            SetConsoleCursorPosition (_screenBuffer, new ((short)col, (short)row));
+            var sb = new StringBuilder ();
+            EscSeqUtils.CSI_AppendCursorPosition (sb, row + 1, col + 1);
+            Write (sb.ToString ());
         }
         }
     }
     }
 
 
@@ -558,7 +535,6 @@ internal partial class WindowsOutput : OutputBase, IOutput
     }
     }
 
 
     private bool _isDisposed;
     private bool _isDisposed;
-    private bool _force16Colors;
     private nint _consoleBuffer;
     private nint _consoleBuffer;
     private readonly StringBuilder _everythingStringBuilder = new ();
     private readonly StringBuilder _everythingStringBuilder = new ();
 
 
@@ -570,7 +546,16 @@ internal partial class WindowsOutput : OutputBase, IOutput
             return;
             return;
         }
         }
 
 
-        if (_isVirtualTerminal)
+        if (IsLegacyConsole)
+        {
+            if (_screenBuffer != nint.Zero)
+            {
+                CloseHandle (_screenBuffer);
+            }
+
+            _screenBuffer = nint.Zero;
+        }
+        else
         {
         {
             if (Environment.GetEnvironmentVariable ("VSAPPIDNAME") is null)
             if (Environment.GetEnvironmentVariable ("VSAPPIDNAME") is null)
             {
             {
@@ -585,15 +570,6 @@ internal partial class WindowsOutput : OutputBase, IOutput
                 Console.Clear ();
                 Console.Clear ();
             }
             }
         }
         }
-        else
-        {
-            if (_screenBuffer != nint.Zero)
-            {
-                CloseHandle (_screenBuffer);
-            }
-
-            _screenBuffer = nint.Zero;
-        }
 
 
         _isDisposed = true;
         _isDisposed = true;
     }
     }

+ 5 - 5
Terminal.Gui/Resources/config.json

@@ -19,8 +19,8 @@
   // --------------- Application Settings ---------------
   // --------------- Application Settings ---------------
   "Key.Separator": "+",
   "Key.Separator": "+",
 
 
+  "Driver.Force16Colors": false,
   "Application.ArrangeKey": "Ctrl+F5",
   "Application.ArrangeKey": "Ctrl+F5",
-  "Application.Force16Colors": false,
   //"Application.ForceDriver": "", // TODO: ForceDriver should be nullable
   //"Application.ForceDriver": "", // TODO: ForceDriver should be nullable
   "Application.IsMouseDisabled": false,
   "Application.IsMouseDisabled": false,
   "Application.NextTabGroupKey": "F6",
   "Application.NextTabGroupKey": "F6",
@@ -136,14 +136,14 @@
                 "Foreground": "White",
                 "Foreground": "White",
                 "Background": "DarkBlue"
                 "Background": "DarkBlue"
               }
               }
-            },
+            }
           },
           },
           {
           {
             "Dialog": {
             "Dialog": {
               "Normal": {
               "Normal": {
                 "Foreground": "BrightBlue",
                 "Foreground": "BrightBlue",
                 "Background": "LightGray"
                 "Background": "LightGray"
-              },
+              }
             }
             }
           },
           },
           {
           {
@@ -152,7 +152,7 @@
                 "Foreground": "White",
                 "Foreground": "White",
                 "Background": "Blue",
                 "Background": "Blue",
                 "Style": "Bold"
                 "Style": "Bold"
-              },
+              }
             }
             }
           },
           },
           {
           {
@@ -161,7 +161,7 @@
                 "Foreground": "Red",
                 "Foreground": "Red",
                 "Background": "WhiteSmoke",
                 "Background": "WhiteSmoke",
                 "Style": "Italic"
                 "Style": "Italic"
-              },
+              }
             }
             }
           }
           }
         ],
         ],

+ 76 - 26
Terminal.Gui/Text/TextFormatter.cs

@@ -226,11 +226,31 @@ public class TextFormatter
             }
             }
 
 
             string strings = linesFormatted [line];
             string strings = linesFormatted [line];
-            string[] graphemes = GraphemeHelper.GetGraphemes (strings).ToArray ();
+            
+            // Use ArrayPool to avoid per-draw allocations
+            int estimatedCount = strings.Length + 10; // Add buffer for grapheme clusters
+            string [] graphemes = ArrayPool<string>.Shared.Rent (estimatedCount);
+            var graphemeCount = 0;
 
 
-            // When text is justified, we lost left or right, so we use the direction to align.
+            try
+            {
+                foreach (string grapheme in GraphemeHelper.GetGraphemes (strings))
+                {
+                    if (graphemeCount >= graphemes.Length)
+                    {
+                        // Need larger array (rare case for complex text)
+                        string [] larger = ArrayPool<string>.Shared.Rent (graphemes.Length * 2);
+                        Array.Copy (graphemes, larger, graphemeCount);
+                        ArrayPool<string>.Shared.Return (graphemes, clearArray: true);
+                        graphemes = larger;
+                    }
+
+                    graphemes [graphemeCount++] = grapheme;
+                }
+
+                // When text is justified, we lost left or right, so we use the direction to align.
 
 
-            int x = 0, y = 0;
+                int x = 0, y = 0;
 
 
             // Horizontal Alignment
             // Horizontal Alignment
             if (Alignment is Alignment.End)
             if (Alignment is Alignment.End)
@@ -317,7 +337,7 @@ public class TextFormatter
             {
             {
                 if (isVertical)
                 if (isVertical)
                 {
                 {
-                    y = screen.Bottom - graphemes.Length;
+                    y = screen.Bottom - graphemeCount;
                 }
                 }
                 else
                 else
                 {
                 {
@@ -353,7 +373,7 @@ public class TextFormatter
             {
             {
                 if (isVertical)
                 if (isVertical)
                 {
                 {
-                    int s = (screen.Height - graphemes.Length) / 2;
+                    int s = (screen.Height - graphemeCount) / 2;
                     y = screen.Top + s;
                     y = screen.Top + s;
                 }
                 }
                 else
                 else
@@ -395,17 +415,17 @@ public class TextFormatter
                         continue;
                         continue;
                     }
                     }
 
 
-                    if (!FillRemaining && idx > graphemes.Length - 1)
+                    if (!FillRemaining && idx > graphemeCount - 1)
                     {
                     {
                         break;
                         break;
                     }
                     }
 
 
                     if ((!isVertical
                     if ((!isVertical
                          && (current - start > maxScreen.Left + maxScreen.Width - screen.X + colOffset
                          && (current - start > maxScreen.Left + maxScreen.Width - screen.X + colOffset
-                             || (idx < graphemes.Length && graphemes [idx].GetColumns () > screen.Width)))
+                             || (idx < graphemeCount && graphemes [idx].GetColumns () > screen.Width)))
                         || (isVertical
                         || (isVertical
                             && ((current > start + size + zeroLengthCount && idx > maxScreen.Top + maxScreen.Height - screen.Y)
                             && ((current > start + size + zeroLengthCount && idx > maxScreen.Top + maxScreen.Height - screen.Y)
-                                || (idx < graphemes.Length && graphemes [idx].GetColumns () > screen.Width))))
+                                || (idx < graphemeCount && graphemes [idx].GetColumns () > screen.Width))))
                     {
                     {
                         break;
                         break;
                     }
                     }
@@ -420,7 +440,7 @@ public class TextFormatter
 
 
                 if (isVertical)
                 if (isVertical)
                 {
                 {
-                    if (idx >= 0 && idx < graphemes.Length)
+                    if (idx >= 0 && idx < graphemeCount)
                     {
                     {
                         text = graphemes [idx];
                         text = graphemes [idx];
                     }
                     }
@@ -471,7 +491,7 @@ public class TextFormatter
                 {
                 {
                     driver?.Move (current, y);
                     driver?.Move (current, y);
 
 
-                    if (idx >= 0 && idx < graphemes.Length)
+                    if (idx >= 0 && idx < graphemeCount)
                     {
                     {
                         text = graphemes [idx];
                         text = graphemes [idx];
                     }
                     }
@@ -531,15 +551,20 @@ public class TextFormatter
                     current += runeWidth;
                     current += runeWidth;
                 }
                 }
 
 
-                int nextRuneWidth = idx + 1 > -1 && idx + 1 < graphemes.Length
+                int nextRuneWidth = idx + 1 > -1 && idx + 1 < graphemeCount
                                         ? graphemes [idx + 1].GetColumns ()
                                         ? graphemes [idx + 1].GetColumns ()
                                         : 0;
                                         : 0;
 
 
-                if (!isVertical && idx + 1 < graphemes.Length && current + nextRuneWidth > start + size)
+                if (!isVertical && idx + 1 < graphemeCount && current + nextRuneWidth > start + size)
                 {
                 {
                     break;
                     break;
                 }
                 }
             }
             }
+            }
+            finally
+            {
+                ArrayPool<string>.Shared.Return (graphemes, clearArray: true);
+            }
         }
         }
     }
     }
 
 
@@ -1042,10 +1067,30 @@ public class TextFormatter
             }
             }
 
 
             string strings = linesFormatted [line];
             string strings = linesFormatted [line];
-            string [] graphemes = GraphemeHelper.GetGraphemes (strings).ToArray ();
+            
+            // Use ArrayPool to avoid per-line allocations
+            int estimatedCount = strings.Length + 10; // Add buffer for grapheme clusters
+            string [] graphemes = ArrayPool<string>.Shared.Rent (estimatedCount);
+            var graphemeCount = 0;
 
 
-            // When text is justified, we lost left or right, so we use the direction to align.
-            int x = 0, y = 0;
+            try
+            {
+                foreach (string grapheme in GraphemeHelper.GetGraphemes (strings))
+                {
+                    if (graphemeCount >= graphemes.Length)
+                    {
+                        // Need larger array (rare case for complex text)
+                        string [] larger = ArrayPool<string>.Shared.Rent (graphemes.Length * 2);
+                        Array.Copy (graphemes, larger, graphemeCount);
+                        ArrayPool<string>.Shared.Return (graphemes, clearArray: true);
+                        graphemes = larger;
+                    }
+
+                    graphemes [graphemeCount++] = grapheme;
+                }
+
+                // When text is justified, we lost left or right, so we use the direction to align.
+                int x = 0, y = 0;
 
 
             switch (Alignment)
             switch (Alignment)
             {
             {
@@ -1122,7 +1167,7 @@ public class TextFormatter
             {
             {
                 // Vertical Alignment
                 // Vertical Alignment
                 case Alignment.End when isVertical:
                 case Alignment.End when isVertical:
-                    y = screen.Bottom - graphemes.Length;
+                    y = screen.Bottom - graphemeCount;
 
 
                     break;
                     break;
                 case Alignment.End:
                 case Alignment.End:
@@ -1152,7 +1197,7 @@ public class TextFormatter
                     }
                     }
                 case Alignment.Center when isVertical:
                 case Alignment.Center when isVertical:
                     {
                     {
-                        int s = (screen.Height - graphemes.Length) / 2;
+                        int s = (screen.Height - graphemeCount) / 2;
                         y = screen.Top + s;
                         y = screen.Top + s;
 
 
                         break;
                         break;
@@ -1192,22 +1237,22 @@ public class TextFormatter
                     continue;
                     continue;
                 }
                 }
 
 
-                if (!FillRemaining && idx > graphemes.Length - 1)
+                if (!FillRemaining && idx > graphemeCount - 1)
                 {
                 {
                     break;
                     break;
                 }
                 }
 
 
                 if ((!isVertical
                 if ((!isVertical
                      && (current - start > maxScreen.Left + maxScreen.Width - screen.X + colOffset
                      && (current - start > maxScreen.Left + maxScreen.Width - screen.X + colOffset
-                         || (idx < graphemes.Length && graphemes [idx].GetColumns () > screen.Width)))
+                         || (idx < graphemeCount && graphemes [idx].GetColumns () > screen.Width)))
                     || (isVertical
                     || (isVertical
                         && ((current > start + size + zeroLengthCount && idx > maxScreen.Top + maxScreen.Height - screen.Y)
                         && ((current > start + size + zeroLengthCount && idx > maxScreen.Top + maxScreen.Height - screen.Y)
-                            || (idx < graphemes.Length && graphemes [idx].GetColumns () > screen.Width))))
+                            || (idx < graphemeCount && graphemes [idx].GetColumns () > screen.Width))))
                 {
                 {
                     break;
                     break;
                 }
                 }
 
 
-                string text = idx >= 0 && idx < graphemes.Length ? graphemes [idx] : " ";
+                string text = idx >= 0 && idx < graphemeCount ? graphemes [idx] : " ";
                 int runeWidth = GetStringWidth (text, TabWidth);
                 int runeWidth = GetStringWidth (text, TabWidth);
 
 
                 if (isVertical)
                 if (isVertical)
@@ -1227,20 +1272,25 @@ public class TextFormatter
 
 
                 current += isVertical && runeWidth > 0 ? 1 : runeWidth;
                 current += isVertical && runeWidth > 0 ? 1 : runeWidth;
 
 
-                int nextStringWidth = idx + 1 > -1 && idx + 1 < graphemes.Length
+                int nextStringWidth = idx + 1 > -1 && idx + 1 < graphemeCount
                                         ? graphemes [idx + 1].GetColumns ()
                                         ? graphemes [idx + 1].GetColumns ()
                                         : 0;
                                         : 0;
 
 
-                if (!isVertical && idx + 1 < graphemes.Length && current + nextStringWidth > start + size)
+                if (!isVertical && idx + 1 < graphemeCount && current + nextStringWidth > start + size)
                 {
                 {
                     break;
                     break;
                 }
                 }
             }
             }
 
 
-            // Add the line's drawn region to the overall region
-            if (lineWidth > 0 && lineHeight > 0)
+                // Add the line's drawn region to the overall region
+                if (lineWidth > 0 && lineHeight > 0)
+                {
+                    drawnRegion.Union (new Rectangle (lineX, lineY, lineWidth, lineHeight));
+                }
+            }
+            finally
             {
             {
-                drawnRegion.Union (new Rectangle (lineX, lineY, lineWidth, lineHeight));
+                ArrayPool<string>.Shared.Return (graphemes, clearArray: true);
             }
             }
         }
         }
 
 

+ 2 - 2
Terminal.Gui/ViewBase/Adornment/Adornment.cs

@@ -88,7 +88,7 @@ public class Adornment : View, IDesignable
     protected override IApplication? GetApp () => Parent?.App;
     protected override IApplication? GetApp () => Parent?.App;
 
 
     /// <inheritdoc />
     /// <inheritdoc />
-    protected override IDriver? GetDriver () => Parent?.Driver ?? base.GetDriver();
+    protected override IDriver? GetDriver () => Parent?.Driver ?? base.GetDriver ();
 
 
     // If a scheme is explicitly set, use that. Otherwise, use the scheme of the parent view.
     // If a scheme is explicitly set, use that. Otherwise, use the scheme of the parent view.
     private Scheme? _scheme;
     private Scheme? _scheme;
@@ -187,7 +187,7 @@ public class Adornment : View, IDesignable
             Thickness.Draw (Driver, ViewportToScreen (Viewport), Diagnostics, ToString ());
             Thickness.Draw (Driver, ViewportToScreen (Viewport), Diagnostics, ToString ());
         }
         }
 
 
-        NeedsDraw = true;
+        SetNeedsDraw ();
 
 
         return true;
         return true;
     }
     }

+ 1 - 3
Terminal.Gui/ViewBase/Adornment/Border.cs

@@ -241,7 +241,7 @@ public partial class Border : Adornment
 
 
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    protected override bool OnDrawingContent ()
+    protected override bool OnDrawingContent (DrawContext? context)
     {
     {
         if (Thickness == Thickness.Empty)
         if (Thickness == Thickness.Empty)
         {
         {
@@ -539,8 +539,6 @@ public partial class Border : Adornment
         }
         }
 
 
         return true;
         return true;
-
-        ;
     }
     }
 
 
     /// <summary>
     /// <summary>

+ 10 - 10
Terminal.Gui/ViewBase/Adornment/Margin.cs

@@ -1,5 +1,6 @@
 
 
 
 
+using System.Diagnostics;
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
 
 
 namespace Terminal.Gui.ViewBase;
 namespace Terminal.Gui.ViewBase;
@@ -62,7 +63,6 @@ public class Margin : Adornment
         }
         }
     }
     }
 
 
-    // PERFORMANCE: Margins are ALWAYS drawn. This may be an issue for apps that have a large number of views with shadows.
     /// <summary>
     /// <summary>
     ///     INTERNAL API - Draws the margins for the specified views. This is called by the <see cref="Application"/> on each
     ///     INTERNAL API - Draws the margins for the specified views. This is called by the <see cref="Application"/> on each
     ///     iteration of the main loop after all Views have been drawn.
     ///     iteration of the main loop after all Views have been drawn.
@@ -75,21 +75,21 @@ public class Margin : Adornment
 
 
         while (stack.Count > 0)
         while (stack.Count > 0)
         {
         {
-            var view = stack.Pop ();
+            View view = stack.Pop ();
 
 
-            if (view.Margin?.GetCachedClip () != null)
+            if (view.Margin is { } margin && margin.Thickness != Thickness.Empty && margin.GetCachedClip () != null)
             {
             {
-                view.Margin!.NeedsDraw = true;
+                margin.SetNeedsDraw ();
                 Region? saved = view.GetClip ();
                 Region? saved = view.GetClip ();
-                view.SetClip (view.Margin!.GetCachedClip ());
-                view.Margin!.Draw (); 
+                view.SetClip (margin.GetCachedClip ());
+                margin.Draw ();
                 view.SetClip (saved);
                 view.SetClip (saved);
-                view.Margin!.ClearCachedClip ();
+                margin.ClearCachedClip ();
             }
             }
 
 
-            view.NeedsDraw = false;
+            view.ClearNeedsDraw ();
 
 
-            foreach (var subview in view.SubViews)
+            foreach (View subview in view.SubViews)
             {
             {
                 stack.Push (subview);
                 stack.Push (subview);
             }
             }
@@ -225,7 +225,7 @@ public class Margin : Adornment
             return;
             return;
         }
         }
 
 
-        bool pressed = args.Value.HasFlag (MouseState.Pressed) && parent.HighlightStates.HasFlag(MouseState.Pressed);
+        bool pressed = args.Value.HasFlag (MouseState.Pressed) && parent.HighlightStates.HasFlag (MouseState.Pressed);
         bool pressedOutside = args.Value.HasFlag (MouseState.PressedOutside) && parent.HighlightStates.HasFlag (MouseState.PressedOutside); ;
         bool pressedOutside = args.Value.HasFlag (MouseState.PressedOutside) && parent.HighlightStates.HasFlag (MouseState.PressedOutside); ;
 
 
         if (pressedOutside)
         if (pressedOutside)

+ 1 - 1
Terminal.Gui/ViewBase/Adornment/ShadowView.cs

@@ -20,7 +20,7 @@ internal class ShadowView : View
     }
     }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    protected override bool OnDrawingContent ()
+    protected override bool OnDrawingContent (DrawContext? context)
     {
     {
         switch (ShadowStyle)
         switch (ShadowStyle)
         {
         {

+ 58 - 6
Terminal.Gui/ViewBase/DrawContext.cs

@@ -1,10 +1,43 @@
-
-namespace Terminal.Gui.ViewBase;
+namespace Terminal.Gui.ViewBase;
 
 
 /// <summary>
 /// <summary>
 ///     Tracks the region that has been drawn during <see cref="View.Draw(DrawContext?)"/>. This is primarily
 ///     Tracks the region that has been drawn during <see cref="View.Draw(DrawContext?)"/>. This is primarily
 ///     in support of <see cref="ViewportSettingsFlags.Transparent"/>.
 ///     in support of <see cref="ViewportSettingsFlags.Transparent"/>.
 /// </summary>
 /// </summary>
+/// <remarks>
+///     <para>
+///         When a <see cref="View"/> has <see cref="ViewportSettingsFlags.Transparent"/> set, the <see cref="DrawContext"/>
+///         is used to track exactly which areas of the screen have been drawn to. After drawing is complete, these drawn
+///         regions are excluded from the clip region, allowing views beneath the transparent view to show through in
+///         the areas that were not drawn.
+///     </para>
+///     <para>
+///         All coordinates tracked by <see cref="DrawContext"/> are in <b>screen-relative coordinates</b>. When reporting
+///         drawn areas from within <see cref="View.OnDrawingContent(DrawContext?)"/>, use <see cref="View.ViewportToScreen(in Rectangle)"/>
+///         or <see cref="View.ContentToScreen(in Point)"/> to convert viewport-relative or content-relative coordinates to
+///         screen-relative coordinates before calling <see cref="AddDrawnRectangle"/> or <see cref="AddDrawnRegion"/>.
+///     </para>
+///     <para>
+///         Example of reporting a non-rectangular drawn region for transparency:
+///     </para>
+///     <code>
+///         protected override bool OnDrawingContent (DrawContext? context)
+///         {
+///             // Draw some content in viewport-relative coordinates
+///             Rectangle rect1 = new Rectangle (5, 5, 10, 3);
+///             Rectangle rect2 = new Rectangle (8, 8, 4, 7);
+///             FillRect (rect1, Glyphs.BlackCircle);
+///             FillRect (rect2, Glyphs.BlackCircle);
+///             
+///             // Report the drawn region in screen-relative coordinates
+///             Region drawnRegion = new Region (ViewportToScreen (rect1));
+///             drawnRegion.Union (ViewportToScreen (rect2));
+///             context?.AddDrawnRegion (drawnRegion);
+///             
+///             return true;
+///         }
+///     </code>
+/// </remarks>
 public class DrawContext
 public class DrawContext
 {
 {
     private readonly Region _drawnRegion = new Region ();
     private readonly Region _drawnRegion = new Region ();
@@ -12,12 +45,20 @@ public class DrawContext
     /// <summary>
     /// <summary>
     /// Gets a copy of the region drawn so far in this context.
     /// Gets a copy of the region drawn so far in this context.
     /// </summary>
     /// </summary>
+    /// <remarks>
+    ///     The returned region contains all areas that have been reported as drawn via <see cref="AddDrawnRectangle"/>
+    ///     or <see cref="AddDrawnRegion"/>, in screen-relative coordinates.
+    /// </remarks>
     public Region GetDrawnRegion () => _drawnRegion.Clone ();
     public Region GetDrawnRegion () => _drawnRegion.Clone ();
 
 
     /// <summary>
     /// <summary>
     /// Reports that a rectangle has been drawn.
     /// Reports that a rectangle has been drawn.
     /// </summary>
     /// </summary>
-    /// <param name="rect">The rectangle that was drawn.</param>
+    /// <param name="rect">The rectangle that was drawn, in screen-relative coordinates.</param>
+    /// <remarks>
+    ///     When called from within <see cref="View.OnDrawingContent(DrawContext?)"/>, ensure the rectangle is in
+    ///     screen-relative coordinates by using <see cref="View.ViewportToScreen(in Rectangle)"/> or similar methods.
+    /// </remarks>
     public void AddDrawnRectangle (Rectangle rect)
     public void AddDrawnRectangle (Rectangle rect)
     {
     {
         _drawnRegion.Combine (rect, RegionOp.Union);
         _drawnRegion.Combine (rect, RegionOp.Union);
@@ -26,7 +67,18 @@ public class DrawContext
     /// <summary>
     /// <summary>
     /// Reports that a region has been drawn.
     /// Reports that a region has been drawn.
     /// </summary>
     /// </summary>
-    /// <param name="region">The region that was drawn.</param>
+    /// <param name="region">The region that was drawn, in screen-relative coordinates.</param>
+    /// <remarks>
+    ///     <para>
+    ///         This method is useful for reporting non-rectangular drawn areas, which is important for
+    ///         proper transparency support with <see cref="ViewportSettingsFlags.Transparent"/>.
+    ///     </para>
+    ///     <para>
+    ///         When called from within <see cref="View.OnDrawingContent(DrawContext?)"/>, ensure the region is in
+    ///         screen-relative coordinates by using <see cref="View.ViewportToScreen(in Rectangle)"/> to convert each
+    ///         rectangle in the region.
+    ///     </para>
+    /// </remarks>
     public void AddDrawnRegion (Region region)
     public void AddDrawnRegion (Region region)
     {
     {
         _drawnRegion.Combine (region, RegionOp.Union);
         _drawnRegion.Combine (region, RegionOp.Union);
@@ -36,7 +88,7 @@ public class DrawContext
     /// Clips (intersects) the drawn region with the specified rectangle.
     /// Clips (intersects) the drawn region with the specified rectangle.
     /// This modifies the internal drawn region directly.
     /// This modifies the internal drawn region directly.
     /// </summary>
     /// </summary>
-    /// <param name="clipRect">The clipping rectangle.</param>
+    /// <param name="clipRect">The clipping rectangle, in screen-relative coordinates.</param>
     public void ClipDrawnRegion (Rectangle clipRect)
     public void ClipDrawnRegion (Rectangle clipRect)
     {
     {
         _drawnRegion.Intersect (clipRect);
         _drawnRegion.Intersect (clipRect);
@@ -46,7 +98,7 @@ public class DrawContext
     /// Clips (intersects) the drawn region with the specified region.
     /// Clips (intersects) the drawn region with the specified region.
     /// This modifies the internal drawn region directly.
     /// This modifies the internal drawn region directly.
     /// </summary>
     /// </summary>
-    /// <param name="clipRegion">The clipping region.</param>
+    /// <param name="clipRegion">The clipping region, in screen-relative coordinates.</param>
     public void ClipDrawnRegion (Region clipRegion)
     public void ClipDrawnRegion (Region clipRegion)
     {
     {
         _drawnRegion.Intersect (clipRegion);
         _drawnRegion.Intersect (clipRegion);

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

@@ -170,16 +170,6 @@ public class Runnable : View, IRunnable
     /// <inheritdoc/>
     /// <inheritdoc/>
     public void RaiseIsModalChangedEvent (bool newIsModal)
     public void RaiseIsModalChangedEvent (bool newIsModal)
     {
     {
-        // CWP Phase 3: Post-notification (work already done by Application)
-        OnIsModalChanged (newIsModal);
-
-        EventArgs<bool> args = new (newIsModal);
-        IsModalChanged?.Invoke (this, args);
-
-        // Layout may need to change when modal state changes
-        SetNeedsLayout ();
-        SetNeedsDraw ();
-
         if (newIsModal)
         if (newIsModal)
         {
         {
             // Set focus to self if becoming modal
             // Set focus to self if becoming modal
@@ -194,6 +184,16 @@ public class Runnable : View, IRunnable
                 App?.Driver?.UpdateCursor ();
                 App?.Driver?.UpdateCursor ();
             }
             }
         }
         }
+
+        // CWP Phase 3: Post-notification (work already done by Application)
+        OnIsModalChanged (newIsModal);
+
+        EventArgs<bool> args = new (newIsModal);
+        IsModalChanged?.Invoke (this, args);
+
+        // Layout may need to change when modal state changes
+        SetNeedsLayout ();
+        SetNeedsDraw ();
     }
     }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>

+ 49 - 0
Terminal.Gui/ViewBase/View.Command.cs

@@ -141,6 +141,13 @@ public partial class View // Command APIs
             Accepting?.Invoke (this, args);
             Accepting?.Invoke (this, args);
         }
         }
 
 
+        // If Accepting was handled, raise Accepted (non-cancelable event)
+        if (args.Handled)
+        {
+            Logging.Debug ($"{Title} ({ctx?.Source?.Title}) - Calling RaiseAccepted");
+            RaiseAccepted (ctx);
+        }
+
         // Accept is a special case where if the event is not canceled, the event is
         // Accept is a special case where if the event is not canceled, the event is
         //  - Invoked on any peer-View with IsDefault == true
         //  - Invoked on any peer-View with IsDefault == true
         //  - bubbled up the SuperView hierarchy.
         //  - bubbled up the SuperView hierarchy.
@@ -201,6 +208,48 @@ public partial class View // Command APIs
     /// </remarks>
     /// </remarks>
     public event EventHandler<CommandEventArgs>? Accepting;
     public event EventHandler<CommandEventArgs>? Accepting;
 
 
+    /// <summary>
+    ///     Raises the <see cref="OnAccepted"/>/<see cref="Accepted"/> event indicating the View has been accepted.
+    ///     This is called after <see cref="Accepting"/> has been raised and not cancelled.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         Unlike <see cref="Accepting"/>, this event cannot be cancelled. It is raised after the View has been accepted.
+    ///     </para>
+    /// </remarks>
+    /// <param name="ctx">The command context.</param>
+    protected void RaiseAccepted (ICommandContext? ctx)
+    {
+        CommandEventArgs args = new () { Context = ctx };
+
+        OnAccepted (args);
+        Accepted?.Invoke (this, args);
+    }
+
+    /// <summary>
+    ///     Called when the View has been accepted. This is called after <see cref="Accepting"/> has been raised and not cancelled.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         Unlike <see cref="OnAccepting"/>, this method is called after the View has been accepted and cannot cancel the operation.
+    ///     </para>
+    /// </remarks>
+    /// <param name="args">The event arguments.</param>
+    protected virtual void OnAccepted (CommandEventArgs args) { }
+
+    /// <summary>
+    ///     Event raised when the View has been accepted. This is raised after <see cref="Accepting"/> has been raised and not cancelled.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         Unlike <see cref="Accepting"/>, this event cannot be cancelled. It is raised after the View has been accepted.
+    ///     </para>
+    ///     <para>
+    ///         See <see cref="RaiseAccepted"/> for more information.
+    ///     </para>
+    /// </remarks>
+    public event EventHandler<CommandEventArgs>? Accepted;
+
     /// <summary>
     /// <summary>
     ///     Called when the user has performed an action (e.g. <see cref="Command.Select"/>) causing the View to change state.
     ///     Called when the user has performed an action (e.g. <see cref="Command.Select"/>) causing the View to change state.
     ///     Calls <see cref="OnSelecting"/> which can be cancelled; if not cancelled raises <see cref="Accepting"/>.
     ///     Calls <see cref="OnSelecting"/> which can be cancelled; if not cancelled raises <see cref="Accepting"/>.

+ 1 - 1
Terminal.Gui/ViewBase/View.Content.cs

@@ -335,7 +335,7 @@ public partial class View
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
     ///         Altering the Viewport Size will eventually (when the view is next laid out) cause the
     ///         Altering the Viewport Size will eventually (when the view is next laid out) cause the
-    ///         <see cref="Layout()"/> and <see cref="OnDrawingContent()"/> methods to be called.
+    ///         <see cref="Layout()"/> and <see cref="OnDrawingContent(DrawContext)"/> methods to be called.
     ///     </para>
     ///     </para>
     /// </remarks>
     /// </remarks>
     public virtual Rectangle Viewport
     public virtual Rectangle Viewport

+ 98 - 212
Terminal.Gui/ViewBase/View.Drawing.cs

@@ -28,8 +28,35 @@ public partial class View // Drawing APIs
             view.Draw (context);
             view.Draw (context);
         }
         }
 
 
-        // Draw the margins (those with Shadows) last to ensure they are drawn on top of the content.
+        // Draw the margins last to ensure they are drawn on top of the content.
         Margin.DrawMargins (viewsArray);
         Margin.DrawMargins (viewsArray);
+
+        // DrawMargins may have caused some views have NeedsDraw/NeedsSubViewDraw set; clear them all.
+        foreach (View view in viewsArray)
+        {
+            view.ClearNeedsDraw ();
+        }
+
+        // After all peer views have been drawn and cleared, we can now clear the SuperView's SubViewNeedsDraw flag.
+        // ClearNeedsDraw() does not clear SuperView.SubViewNeedsDraw (by design, to avoid premature clearing
+        // when siblings still need drawing), so we must do it here after ALL peers are processed.
+        // We only clear the flag if ALL the SuperView's subviews no longer need drawing.
+        View? lastSuperView = null;
+        foreach (View view in viewsArray)
+        {
+            if (view is not Adornment && view.SuperView is { } && view.SuperView != lastSuperView)
+            {
+                // Check if ANY subview of this SuperView still needs drawing
+                bool anySubViewNeedsDrawing = view.SuperView.InternalSubViews.Any (v => v.NeedsDraw || v.SubViewNeedsDraw);
+
+                if (!anySubViewNeedsDrawing)
+                {
+                    view.SuperView.SubViewNeedsDraw = false;
+                }
+
+                lastSuperView = view.SuperView;
+            }
+        }
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -73,7 +100,7 @@ public partial class View // Drawing APIs
             originalClip = AddViewportToClip ();
             originalClip = AddViewportToClip ();
 
 
             // If no context ...
             // If no context ...
-            context ??= new DrawContext ();
+            context ??= new ();
 
 
             SetAttributeForRole (Enabled ? VisualRole.Normal : VisualRole.Disabled);
             SetAttributeForRole (Enabled ? VisualRole.Normal : VisualRole.Disabled);
             DoClearViewport (context);
             DoClearViewport (context);
@@ -136,7 +163,6 @@ public partial class View // Drawing APIs
 
 
         // ------------------------------------
         // ------------------------------------
         // This causes the Margin to be drawn in a second pass if it has a ShadowStyle
         // This causes the Margin to be drawn in a second pass if it has a ShadowStyle
-        // PERFORMANCE: If there is a Margin w/ Shadow, it will be redrawn each iteration of the main loop.
         Margin?.CacheClip ();
         Margin?.CacheClip ();
 
 
         // ------------------------------------
         // ------------------------------------
@@ -154,7 +180,7 @@ public partial class View // Drawing APIs
     {
     {
         // NOTE: We do not support subviews of Margin?
         // NOTE: We do not support subviews of Margin?
 
 
-        if (Border?.SubViews is { } && Border.Thickness != Thickness.Empty)
+        if (Border?.SubViews is { } && Border.Thickness != Thickness.Empty && Border.NeedsDraw)
         {
         {
             // PERFORMANCE: Get the check for DrawIndicator out of this somehow.
             // PERFORMANCE: Get the check for DrawIndicator out of this somehow.
             foreach (View subview in Border.SubViews.Where (v => v.Visible || v.Id == "DrawIndicator"))
             foreach (View subview in Border.SubViews.Where (v => v.Visible || v.Id == "DrawIndicator"))
@@ -172,7 +198,7 @@ public partial class View // Drawing APIs
             SetClip (saved);
             SetClip (saved);
         }
         }
 
 
-        if (Padding?.SubViews is { } && Padding.Thickness != Thickness.Empty)
+        if (Padding?.SubViews is { } && Padding.Thickness != Thickness.Empty && Padding.NeedsDraw)
         {
         {
             foreach (View subview in Padding.SubViews)
             foreach (View subview in Padding.SubViews)
             {
             {
@@ -206,7 +232,7 @@ public partial class View // Drawing APIs
         {
         {
             Margin.NeedsLayout = false;
             Margin.NeedsLayout = false;
             Margin?.Thickness.Draw (Driver, FrameToScreen ());
             Margin?.Thickness.Draw (Driver, FrameToScreen ());
-            Margin?.Parent?.SetSubViewNeedsDraw ();
+            Margin?.Parent?.SetSubViewNeedsDrawDownHierarchy ();
         }
         }
 
 
         if (SubViewNeedsDraw)
         if (SubViewNeedsDraw)
@@ -253,7 +279,7 @@ public partial class View // Drawing APIs
 
 
         if (Margin is { } && Margin.Thickness != Thickness.Empty/* && Margin.ShadowStyle == ShadowStyle.None*/)
         if (Margin is { } && Margin.Thickness != Thickness.Empty/* && Margin.ShadowStyle == ShadowStyle.None*/)
         {
         {
-           //Margin?.Draw ();
+            //Margin?.Draw ();
         }
         }
     }
     }
 
 
@@ -288,7 +314,7 @@ public partial class View // Drawing APIs
 
 
     internal void DoClearViewport (DrawContext? context = null)
     internal void DoClearViewport (DrawContext? context = null)
     {
     {
-        if (ViewportSettings.HasFlag (ViewportSettingsFlags.Transparent) || OnClearingViewport ())
+        if (!NeedsDraw || ViewportSettings.HasFlag (ViewportSettingsFlags.Transparent) || OnClearingViewport ())
         {
         {
             return;
             return;
         }
         }
@@ -407,8 +433,8 @@ public partial class View // Drawing APIs
 
 
         DrawText (context);
         DrawText (context);
 
 
-        OnDrewText();
-        DrewText?.Invoke(this, EventArgs.Empty);
+        OnDrewText ();
+        DrewText?.Invoke (this, EventArgs.Empty);
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -447,7 +473,7 @@ public partial class View // Drawing APIs
 
 
         if (Driver is { })
         if (Driver is { })
         {
         {
-            TextFormatter?.Draw (
+            TextFormatter.Draw (
                                  Driver,
                                  Driver,
                                  drawRect,
                                  drawRect,
                                  HasFocus ? GetAttributeForRole (VisualRole.Focus) : GetAttributeForRole (VisualRole.Normal),
                                  HasFocus ? GetAttributeForRole (VisualRole.Focus) : GetAttributeForRole (VisualRole.Normal),
@@ -456,7 +482,7 @@ public partial class View // Drawing APIs
         }
         }
 
 
         // We assume that the text has been drawn over the entire area; ensure that the subviews are redrawn.
         // We assume that the text has been drawn over the entire area; ensure that the subviews are redrawn.
-        SetSubViewNeedsDraw ();
+        SetSubViewNeedsDrawDownHierarchy ();
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -468,17 +494,12 @@ public partial class View // Drawing APIs
     public event EventHandler? DrewText;
     public event EventHandler? DrewText;
 
 
     #endregion DrawText
     #endregion DrawText
+
     #region DrawContent
     #region DrawContent
 
 
     private void DoDrawContent (DrawContext? context = null)
     private void DoDrawContent (DrawContext? context = null)
     {
     {
-        if (OnDrawingContent (context))
-        {
-            return;
-        }
-
-        // TODO: Upgrade all overrides of OnDrawingContent to use DrawContext and remove this override
-        if (OnDrawingContent ())
+        if (!NeedsDraw || OnDrawingContent (context))
         {
         {
             return;
             return;
         }
         }
@@ -499,20 +520,66 @@ public partial class View // Drawing APIs
     /// </summary>
     /// </summary>
     /// <param name="context">The draw context to report drawn areas to.</param>
     /// <param name="context">The draw context to report drawn areas to.</param>
     /// <returns><see langword="true"/> to stop further drawing content.</returns>
     /// <returns><see langword="true"/> to stop further drawing content.</returns>
+    /// <remarks>
+    ///     <para>
+    ///         Override this method to draw custom content for your View.
+    ///     </para>
+    ///     <para>
+    ///         <b>Transparency Support:</b> If your View has <see cref="ViewportSettings"/> with <see cref="ViewportSettingsFlags.Transparent"/>
+    ///         set, you should report the exact regions you draw to via the <paramref name="context"/> parameter. This allows
+    ///         the transparency system to exclude only the drawn areas from the clip region, letting views beneath show through
+    ///         in the areas you didn't draw.
+    ///     </para>
+    ///     <para>
+    ///         Use <see cref="DrawContext.AddDrawnRectangle"/> for simple rectangular areas, or <see cref="DrawContext.AddDrawnRegion"/>
+    ///         for complex, non-rectangular shapes. All coordinates passed to these methods must be in <b>screen-relative coordinates</b>.
+    ///         Use <see cref="View.ViewportToScreen(in Rectangle)"/> or <see cref="View.ContentToScreen(in Point)"/> to convert from
+    ///         viewport-relative or content-relative coordinates.
+    ///     </para>
+    ///     <para>
+    ///         Example of drawing custom content with transparency support:
+    ///     </para>
+    ///     <code>
+    ///         protected override bool OnDrawingContent (DrawContext? context)
+    ///         {
+    ///             base.OnDrawingContent (context);
+    ///             
+    ///             // Draw content in viewport-relative coordinates
+    ///             Rectangle rect1 = new Rectangle (5, 5, 10, 3);
+    ///             Rectangle rect2 = new Rectangle (8, 8, 4, 7);
+    ///             FillRect (rect1, Glyphs.BlackCircle);
+    ///             FillRect (rect2, Glyphs.BlackCircle);
+    ///             
+    ///             // Report drawn region in screen-relative coordinates for transparency
+    ///             if (ViewportSettings.HasFlag (ViewportSettingsFlags.Transparent))
+    ///             {
+    ///                 Region drawnRegion = new Region (ViewportToScreen (rect1));
+    ///                 drawnRegion.Union (ViewportToScreen (rect2));
+    ///                 context?.AddDrawnRegion (drawnRegion);
+    ///             }
+    ///             
+    ///             return true;
+    ///         }
+    ///     </code>
+    /// </remarks>
     protected virtual bool OnDrawingContent (DrawContext? context) { return false; }
     protected virtual bool OnDrawingContent (DrawContext? context) { return false; }
 
 
-    /// <summary>
-    ///     Called when the View's content is to be drawn. The default implementation does nothing.
-    /// </summary>
-    /// <returns><see langword="true"/> to stop further drawing content.</returns>
-    protected virtual bool OnDrawingContent () { return false; }
-
     /// <summary>Raised when the View's content is to be drawn.</summary>
     /// <summary>Raised when the View's content is to be drawn.</summary>
     /// <remarks>
     /// <remarks>
-    ///     <para>Will be invoked before any subviews added with <see cref="Add(View)"/> have been drawn.</para>
     ///     <para>
     ///     <para>
-    ///         Rect provides the view-relative rectangle describing the currently visible viewport into the
-    ///         <see cref="View"/> .
+    ///         Subscribe to this event to draw custom content for the View. Use the drawing methods available on <see cref="View"/>
+    ///         such as <see cref="View.AddRune(int, int, Rune)"/>, <see cref="View.AddStr(string)"/>, and <see cref="View.FillRect(Rectangle, Rune)"/>.
+    ///     </para>
+    ///     <para>
+    ///         The event is invoked after <see cref="ClearingViewport"/> and <see cref="Text"/> have been drawn, but before any <see cref="SubViews"/> are drawn.
+    ///     </para>
+    ///     <para>
+    ///         <b>Transparency Support:</b> If the View has <see cref="ViewportSettings"/> with <see cref="ViewportSettingsFlags.Transparent"/>
+    ///         set, use the <see cref="DrawEventArgs.DrawContext"/> to report which areas were actually drawn. This enables proper transparency
+    ///         by excluding only the drawn areas from the clip region. See <see cref="DrawContext"/> for details on reporting drawn regions.
+    ///     </para>
+    ///     <para>
+    ///         The <see cref="DrawEventArgs.NewViewport"/> property provides the view-relative rectangle describing the currently visible viewport into the View.
     ///     </para>
     ///     </para>
     /// </remarks>
     /// </remarks>
     public event EventHandler<DrawEventArgs>? DrawingContent;
     public event EventHandler<DrawEventArgs>? DrawingContent;
@@ -523,7 +590,7 @@ public partial class View // Drawing APIs
 
 
     private void DoDrawSubViews (DrawContext? context = null)
     private void DoDrawSubViews (DrawContext? context = null)
     {
     {
-        if (OnDrawingSubViews (context))
+        if (!NeedsDraw || OnDrawingSubViews (context))
         {
         {
             return;
             return;
         }
         }
@@ -589,7 +656,7 @@ public partial class View // Drawing APIs
             // TODO: HACK - This forcing of SetNeedsDraw with SuperViewRendersLineCanvas enables auto line join to work, but is brute force.
             // TODO: HACK - This forcing of SetNeedsDraw with SuperViewRendersLineCanvas enables auto line join to work, but is brute force.
             if (view.SuperViewRendersLineCanvas || view.ViewportSettings.HasFlag (ViewportSettingsFlags.Transparent))
             if (view.SuperViewRendersLineCanvas || view.ViewportSettings.HasFlag (ViewportSettingsFlags.Transparent))
             {
             {
-                view.SetNeedsDraw ();
+                //view.SetNeedsDraw ();
             }
             }
             view.Draw (context);
             view.Draw (context);
 
 
@@ -607,7 +674,7 @@ public partial class View // Drawing APIs
 
 
     private void DoRenderLineCanvas ()
     private void DoRenderLineCanvas ()
     {
     {
-        if (OnRenderingLineCanvas ())
+        if (!NeedsDraw || OnRenderingLineCanvas ())
         {
         {
             return;
             return;
         }
         }
@@ -733,185 +800,4 @@ public partial class View // Drawing APIs
 
 
     #endregion DrawComplete
     #endregion DrawComplete
 
 
-    #region NeedsDraw
-
-    // TODO: Change NeedsDraw to use a Region instead of Rectangle
-    // TODO: Make _needsDrawRect nullable instead of relying on Empty
-    //      TODO: If null, it means ?
-    //      TODO: If Empty, it means no need to redraw
-    //      TODO: If not Empty, it means the region that needs to be redrawn
-    // The viewport-relative region that needs to be redrawn. Marked internal for unit tests.
-    internal Rectangle NeedsDrawRect { get; set; } = Rectangle.Empty;
-
-    /// <summary>Gets or sets whether the view needs to be redrawn.</summary>
-    /// <remarks>
-    ///     <para>
-    ///         Will be <see langword="true"/> if the <see cref="NeedsLayout"/> property is <see langword="true"/> or if
-    ///         any part of the view's <see cref="Viewport"/> needs to be redrawn.
-    ///     </para>
-    ///     <para>
-    ///         Setting has no effect on <see cref="NeedsLayout"/>.
-    ///     </para>
-    /// </remarks>
-    public bool NeedsDraw
-    {
-        get => Visible && (NeedsDrawRect != Rectangle.Empty || Margin?.NeedsDraw == true || Border?.NeedsDraw == true || Padding?.NeedsDraw == true);
-        set
-        {
-            if (value)
-            {
-                SetNeedsDraw ();
-            }
-            else
-            {
-                ClearNeedsDraw ();
-            }
-        }
-    }
-
-    /// <summary>Gets whether any SubViews need to be redrawn.</summary>
-    public bool SubViewNeedsDraw { get; private set; }
-
-    /// <summary>Sets that the <see cref="Viewport"/> of this View needs to be redrawn.</summary>
-    /// <remarks>
-    ///     If the view has not been initialized (<see cref="IsInitialized"/> is <see langword="false"/>), this method
-    ///     does nothing.
-    /// </remarks>
-    public void SetNeedsDraw ()
-    {
-        Rectangle viewport = Viewport;
-
-        if (!Visible || (NeedsDrawRect != Rectangle.Empty && viewport.IsEmpty))
-        {
-            // This handles the case where the view has not been initialized yet
-            return;
-        }
-
-        SetNeedsDraw (viewport);
-    }
-
-    /// <summary>Expands the area of this view needing to be redrawn to include <paramref name="viewPortRelativeRegion"/>.</summary>
-    /// <remarks>
-    ///     <para>
-    ///         The location of <paramref name="viewPortRelativeRegion"/> is relative to the View's <see cref="Viewport"/>.
-    ///     </para>
-    ///     <para>
-    ///         If the view has not been initialized (<see cref="IsInitialized"/> is <see langword="false"/>), the area to be
-    ///         redrawn will be the <paramref name="viewPortRelativeRegion"/>.
-    ///     </para>
-    /// </remarks>
-    /// <param name="viewPortRelativeRegion">The <see cref="Viewport"/>relative region that needs to be redrawn.</param>
-    public void SetNeedsDraw (Rectangle viewPortRelativeRegion)
-    {
-        if (!Visible)
-        {
-            return;
-        }
-
-        if (NeedsDrawRect.IsEmpty)
-        {
-            NeedsDrawRect = viewPortRelativeRegion;
-        }
-        else
-        {
-            int x = Math.Min (Viewport.X, viewPortRelativeRegion.X);
-            int y = Math.Min (Viewport.Y, viewPortRelativeRegion.Y);
-            int w = Math.Max (Viewport.Width, viewPortRelativeRegion.Width);
-            int h = Math.Max (Viewport.Height, viewPortRelativeRegion.Height);
-            NeedsDrawRect = new (x, y, w, h);
-        }
-
-        // Do not set on Margin - it will be drawn in a separate pass.
-
-        if (Border is { } && Border.Thickness != Thickness.Empty)
-        {
-            Border?.SetNeedsDraw ();
-        }
-
-        if (Padding is { } && Padding.Thickness != Thickness.Empty)
-        {
-            Padding?.SetNeedsDraw ();
-        }
-
-        SuperView?.SetSubViewNeedsDraw ();
-
-        if (this is Adornment adornment)
-        {
-            adornment.Parent?.SetSubViewNeedsDraw ();
-        }
-
-        // There was multiple enumeration error here, so calling new snapshot collection - probably a stop gap
-        foreach (View subview in InternalSubViews.Snapshot ())
-        {
-            if (subview.Frame.IntersectsWith (viewPortRelativeRegion))
-            {
-                Rectangle subviewRegion = Rectangle.Intersect (subview.Frame, viewPortRelativeRegion);
-                subviewRegion.X -= subview.Frame.X;
-                subviewRegion.Y -= subview.Frame.Y;
-                subview.SetNeedsDraw (subviewRegion);
-            }
-        }
-    }
-
-    /// <summary>Sets <see cref="SubViewNeedsDraw"/> to <see langword="true"/> for this View and all Superviews.</summary>
-    public void SetSubViewNeedsDraw ()
-    {
-        if (!Visible)
-        {
-            return;
-        }
-
-        SubViewNeedsDraw = true;
-
-        if (this is Adornment adornment)
-        {
-            adornment.Parent?.SetSubViewNeedsDraw ();
-        }
-
-        if (SuperView is { SubViewNeedsDraw: false })
-        {
-            SuperView.SetSubViewNeedsDraw ();
-        }
-    }
-
-    /// <summary>Clears <see cref="NeedsDraw"/> and <see cref="SubViewNeedsDraw"/>.</summary>
-    protected void ClearNeedsDraw ()
-    {
-        NeedsDrawRect = Rectangle.Empty;
-        SubViewNeedsDraw = false;
-
-        if (Margin is { } && (Margin.Thickness != Thickness.Empty || Margin.SubViewNeedsDraw || Margin.NeedsDraw))
-        {
-            Margin?.ClearNeedsDraw ();
-        }
-
-        if (Border is { } && (Border.Thickness != Thickness.Empty || Border.SubViewNeedsDraw || Border.NeedsDraw))
-        {
-            Border?.ClearNeedsDraw ();
-        }
-
-        if (Padding is { } && (Padding.Thickness != Thickness.Empty || Padding.SubViewNeedsDraw || Padding.NeedsDraw))
-        {
-            Padding?.ClearNeedsDraw ();
-        }
-
-        // There was multiple enumeration error here, so calling new snapshot collection - probably a stop gap
-        foreach (View subview in InternalSubViews.Snapshot ())
-        {
-            subview.ClearNeedsDraw ();
-        }
-
-        if (SuperView is { })
-        {
-            SuperView.SubViewNeedsDraw = false;
-        }
-
-        // This ensures LineCanvas' get redrawn
-        if (!SuperViewRendersLineCanvas)
-        {
-            LineCanvas.Clear ();
-        }
-    }
-
-    #endregion NeedsDraw
 }
 }

+ 168 - 0
Terminal.Gui/ViewBase/View.NeedsDraw.cs

@@ -0,0 +1,168 @@
+namespace Terminal.Gui.ViewBase;
+
+public partial class View
+{
+    // NOTE: NeedsDrawRect is not currently used to clip drawing to only the invalidated region.
+    //       It is only used within SetNeedsDraw to propagate redraw requests to subviews.
+    // NOTE: Consider changing NeedsDrawRect from Rectangle to Region for more precise invalidation
+    //       NeedsDraw is already efficiently cached via NeedsDrawRect. It checks:
+    //       1. NeedsDrawRect (cached by SetNeedsDraw/ClearNeedsDraw)
+    //       2. Adornment NeedsDraw flags (each cached separately)
+    /// <summary>
+    ///     INTERNAL: Gets the viewport-relative region that needs to be redrawn.
+    /// </summary>
+    internal Rectangle NeedsDrawRect { get; private set; } = Rectangle.Empty;
+
+    /// <summary>Gets whether the view needs to be redrawn.</summary>
+    /// <remarks>
+    ///     <para>
+    ///         Will be <see langword="true"/> if the <see cref="NeedsLayout"/> property is <see langword="true"/> or if
+    ///         any part of the view's <see cref="Viewport"/> needs to be redrawn.
+    ///     </para>
+    /// </remarks>
+    public bool NeedsDraw => Visible && (NeedsDrawRect != Rectangle.Empty || Margin?.NeedsDraw == true || Border?.NeedsDraw == true || Padding?.NeedsDraw == true);
+
+    /// <summary>Sets <see cref="NeedsDraw"/> to <see langword="true"/> indicating the <see cref="Viewport"/> of this View needs to be redrawn.</summary>
+    /// <remarks>
+    ///     If the view is not visible (<see cref="Visible"/> is <see langword="false"/>), this method
+    ///     does nothing.
+    /// </remarks>
+    public void SetNeedsDraw ()
+    {
+        Rectangle viewport = Viewport;
+
+        if (!Visible || (NeedsDrawRect != Rectangle.Empty && viewport.IsEmpty))
+        {
+            // This handles the case where the view has not been initialized yet
+            return;
+        }
+
+        SetNeedsDraw (viewport);
+    }
+
+    /// <summary>Expands the area of this view needing to be redrawn to include <paramref name="viewPortRelativeRegion"/>.</summary>
+    /// <remarks>
+    ///     <para>
+    ///         The location of <paramref name="viewPortRelativeRegion"/> is relative to the View's <see cref="Viewport"/>.
+    ///     </para>
+    ///     <para>
+    ///         If the view has not been initialized (<see cref="IsInitialized"/> is <see langword="false"/>), the area to be
+    ///         redrawn will be the <paramref name="viewPortRelativeRegion"/>.
+    ///     </para>
+    /// </remarks>
+    /// <param name="viewPortRelativeRegion">The <see cref="Viewport"/>relative region that needs to be redrawn.</param>
+    public void SetNeedsDraw (Rectangle viewPortRelativeRegion)
+    {
+        if (!Visible)
+        {
+            return;
+        }
+
+        if (NeedsDrawRect.IsEmpty)
+        {
+            NeedsDrawRect = viewPortRelativeRegion;
+        }
+        else
+        {
+            int x = Math.Min (Viewport.X, viewPortRelativeRegion.X);
+            int y = Math.Min (Viewport.Y, viewPortRelativeRegion.Y);
+            int w = Math.Max (Viewport.Width, viewPortRelativeRegion.Width);
+            int h = Math.Max (Viewport.Height, viewPortRelativeRegion.Height);
+            NeedsDrawRect = new (x, y, w, h);
+        }
+
+        // Do not set on Margin - it will be drawn in a separate pass.
+
+        if (Border is { } && Border.Thickness != Thickness.Empty)
+        {
+            Border?.SetNeedsDraw ();
+        }
+
+        if (Padding is { } && Padding.Thickness != Thickness.Empty)
+        {
+            Padding?.SetNeedsDraw ();
+        }
+
+        SuperView?.SetSubViewNeedsDrawDownHierarchy ();
+
+        if (this is Adornment adornment)
+        {
+            adornment.Parent?.SetSubViewNeedsDrawDownHierarchy ();
+        }
+
+        foreach (View subview in InternalSubViews.Snapshot ())
+        {
+            if (subview.Frame.IntersectsWith (viewPortRelativeRegion))
+            {
+                Rectangle subviewRegion = Rectangle.Intersect (subview.Frame, viewPortRelativeRegion);
+                subviewRegion.X -= subview.Frame.X;
+                subviewRegion.Y -= subview.Frame.Y;
+                subview.SetNeedsDraw (subviewRegion);
+            }
+        }
+    }
+
+    /// <summary>INTERNAL: Clears <see cref="NeedsDraw"/> and <see cref="SubViewNeedsDraw"/> for this view and all SubViews.</summary>
+    /// <remarks>
+    ///     See <see cref="SubViewNeedsDraw"/> is a cached value that is set when any subview or adornment requests a redraw.
+    ///     It may not always be in sync with the actual state of the subviews.
+    /// </remarks>
+    internal void ClearNeedsDraw ()
+    {
+        NeedsDrawRect = Rectangle.Empty;
+
+        Margin?.ClearNeedsDraw ();
+        Border?.ClearNeedsDraw ();
+        Padding?.ClearNeedsDraw ();
+
+        foreach (View subview in InternalSubViews.Snapshot ())
+        {
+            subview.ClearNeedsDraw ();
+        }
+
+        SubViewNeedsDraw = false;
+
+        // This ensures LineCanvas' get redrawn
+        if (!SuperViewRendersLineCanvas)
+        {
+            LineCanvas.Clear ();
+        }
+    }
+
+    // NOTE: SubViewNeedsDraw is decoupled from the actual state of the subviews (and adornments).
+    //       It is a performance optimization to avoid having to traverse all subviews and adornments to check if any need redraw.
+    //       As a result the code is fragile and can get out of sync; care must be taken to ensure it is set and cleared correctly.
+    /// <summary>
+    ///     INTERNAL: Gets whether any SubViews need to be redrawn.
+    /// </summary>
+    /// <remarks>
+    ///     See <see cref="SubViewNeedsDraw"/> is a cached value that is set when any subview or adornment requests a redraw.
+    ///     It may not always be in sync with the actual state of the subviews.
+    /// </remarks>
+    internal bool SubViewNeedsDraw { get; private set; }
+
+    /// <summary>INTERNAL: Sets <see cref="SubViewNeedsDraw"/> to <see langword="true"/> for this View and all Superviews.</summary>
+    /// <remarks>
+    ///     See <see cref="SubViewNeedsDraw"/> is a cached value that is set when any subview or adornment requests a redraw.
+    ///     It may not always be in sync with the actual state of the subviews.
+    /// </remarks>
+    internal void SetSubViewNeedsDrawDownHierarchy ()
+    {
+        if (!Visible)
+        {
+            return;
+        }
+
+        SubViewNeedsDraw = true;
+
+        if (this is Adornment adornment)
+        {
+            adornment.Parent?.SetSubViewNeedsDrawDownHierarchy ();
+        }
+
+        if (SuperView is { SubViewNeedsDraw: false })
+        {
+            SuperView.SetSubViewNeedsDrawDownHierarchy ();
+        }
+    }
+}

+ 0 - 79
Terminal.Gui/Views/Autocomplete/Autocomplete.cd

@@ -1,79 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<ClassDiagram MajorVersion="1" MinorVersion="1">
-  <Class Name="Terminal.Gui.Views.AppendAutocomplete" Collapsed="true">
-    <Position X="0.5" Y="6.5" Width="2" />
-    <TypeIdentifier>
-      <HashCode>AAAgAABAAQIAAAAAAAAAAAAABAAAIAQAgAEIAggAIAA=</HashCode>
-      <FileName>Core\Autocomplete\AppendAutocomplete.cs</FileName>
-    </TypeIdentifier>
-  </Class>
-  <Class Name="Terminal.Gui.Views.AutocompleteBase" Collapsed="true">
-    <Position X="1.75" Y="5.25" Width="1.5" />
-    <TypeIdentifier>
-      <HashCode>AAQgAAAAAUAAIAAAAAIAAAAAAAEAIAQIgQAIQAAAMBA=</HashCode>
-      <FileName>Core\Autocomplete\AutocompleteBase.cs</FileName>
-    </TypeIdentifier>
-    <Lollipop Position="0.2" />
-  </Class>
-  <Class Name="Terminal.Gui.Views.PopupAutocomplete" Collapsed="true">
-    <Position X="2.75" Y="6.5" Width="1.5" />
-    <NestedTypes>
-      <Class Name="Terminal.Gui.Views.PopupAutocomplete.Popup" Collapsed="true">
-        <TypeIdentifier>
-          <NewMemberFileName>Core\Autocomplete\PopupAutocomplete.cs</NewMemberFileName>
-        </TypeIdentifier>
-      </Class>
-    </NestedTypes>
-    <TypeIdentifier>
-      <HashCode>IAEhAAQAASBEQAAAAAIBAAgYAAAAIAwAwKAAQACBAAA=</HashCode>
-      <FileName>Core\Autocomplete\PopupAutocomplete.cs</FileName>
-    </TypeIdentifier>
-  </Class>
-  <Class Name="Terminal.Gui.Views.SingleWordSuggestionGenerator" BaseTypeListCollapsed="true">
-    <Position X="6.25" Y="3.5" Width="3" />
-    <TypeIdentifier>
-      <HashCode>CEAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAIAA=</HashCode>
-      <FileName>Core\Autocomplete\SingleWordSuggestionGenerator.cs</FileName>
-    </TypeIdentifier>
-    <Lollipop Position="0.2" />
-  </Class>
-  <Class Name="Terminal.Gui.Views.Suggestion">
-    <Position X="4.5" Y="2.5" Width="1.5" />
-    <TypeIdentifier>
-      <HashCode>AAAAAAAAAAAAAEAAAAAABAAAAAAAAAAAAAAAAAAAAAE=</HashCode>
-      <FileName>Core\Autocomplete\Suggestion.cs</FileName>
-    </TypeIdentifier>
-  </Class>
-  <Class Name="Terminal.Gui.Views.TextFieldAutocomplete" Collapsed="true">
-    <Position X="1.5" Y="7.5" Width="2" />
-    <TypeIdentifier>
-      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAgAAAAAAAAAA=</HashCode>
-      <FileName>Views\TextField.cs</FileName>
-    </TypeIdentifier>
-  </Class>
-  <Class Name="Terminal.Gui.Views.TextViewAutocomplete" Collapsed="true">
-    <Position X="3.75" Y="7.5" Width="2.25" />
-    <TypeIdentifier>
-      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAgAAAAAAAAAA=</HashCode>
-      <FileName>Views\TextView.cs</FileName>
-    </TypeIdentifier>
-  </Class>
-  <Interface Name="Terminal.Gui.Views.IAutocomplete">
-    <Position X="1.75" Y="0.5" Width="2.5" />
-    <TypeIdentifier>
-      <HashCode>AAQgAAAAAUAAIAAAAAAAAAAAAAEAIAQIgQAIQAAAMBA=</HashCode>
-      <FileName>Core\Autocomplete\IAutocomplete.cs</FileName>
-    </TypeIdentifier>
-    <ShowAsAssociation>
-      <Property Name="SuggestionGenerator" />
-    </ShowAsAssociation>
-  </Interface>
-  <Interface Name="Terminal.Gui.Views.ISuggestionGenerator">
-    <Position X="6.25" Y="1.75" Width="2.25" />
-    <TypeIdentifier>
-      <HashCode>AEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAA=</HashCode>
-      <FileName>Core\Autocomplete\ISuggestionGenerator.cs</FileName>
-    </TypeIdentifier>
-  </Interface>
-  <Font Name="Segoe UI" Size="9" />
-</ClassDiagram>

+ 1 - 1
Terminal.Gui/Views/Autocomplete/PopupAutocomplete.PopUp.cs

@@ -15,7 +15,7 @@ public abstract partial class PopupAutocomplete
 
 
         private readonly PopupAutocomplete _autoComplete;
         private readonly PopupAutocomplete _autoComplete;
 
 
-        protected override bool OnDrawingContent ()
+        protected override bool OnDrawingContent (DrawContext? context)
         {
         {
             if (!_autoComplete.LastPopupPos.HasValue)
             if (!_autoComplete.LastPopupPos.HasValue)
             {
             {

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

@@ -580,7 +580,7 @@ public class CharMap : View, IDesignable
     private static int RowLabelWidth => $"U+{MAX_CODE_POINT:x5}".Length + 1;
     private static int RowLabelWidth => $"U+{MAX_CODE_POINT:x5}".Length + 1;
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    protected override bool OnDrawingContent ()
+    protected override bool OnDrawingContent (DrawContext? context)
     {
     {
         if (Viewport.Height == 0 || Viewport.Width == 0)
         if (Viewport.Height == 0 || Viewport.Width == 0)
         {
         {

+ 0 - 124
Terminal.Gui/Views/CollectionNavigation/CollectionNavigation.cd

@@ -1,124 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<ClassDiagram MajorVersion="1" MinorVersion="1">
-  <Comment CommentText="Views that use the CollectionNavigation system">
-    <Position X="0.5" Y="0.5" Height="0.458" Width="1.856" />
-  </Comment>
-  <Comment CommentText="Specialized navigators for each collection type (e.g. list, tree etc)">
-    <Position X="4.646" Y="0.5" Height="0.5" Width="3.169" />
-  </Comment>
-  <Comment CommentText="Shared matching component (users should provide alternative implementations of this class if they want to modify collection navigation behaviour)">
-    <Position X="9.448" Y="0.5" Height="0.708" Width="3.169" />
-  </Comment>
-  <Class Name="Terminal.Gui.Views.CollectionNavigatorBase" Collapsed="true">
-    <Position X="6.25" Y="1.5" Width="2" />
-    <TypeIdentifier>
-      <HashCode>AAgEAAAAAAAQAAAIAAEAAgAAAAAABAAEAAAAACwAAAA=</HashCode>
-      <FileName>Views\CollectionNavigation\CollectionNavigatorBase.cs</FileName>
-    </TypeIdentifier>
-    <ShowAsAssociation>
-      <Property Name="Matcher" />
-    </ShowAsAssociation>
-    <Lollipop Position="0.2" />
-  </Class>
-  <Class Name="Terminal.Gui.Views.CollectionNavigator" Collapsed="true">
-    <Position X="4.5" Y="3.5" Width="2" />
-    <TypeIdentifier>
-      <HashCode>AAAAAAAAAAAAQAAAAAAAAgAAAAAAAAAEAAAAAAAAAAA=</HashCode>
-      <FileName>Views\CollectionNavigation\CollectionNavigator.cs</FileName>
-    </TypeIdentifier>
-    <Lollipop Position="0.2" />
-  </Class>
-  <Class Name="Terminal.Gui.Views.DefaultCollectionNavigatorMatcher">
-    <Position X="9.5" Y="2.5" Width="2.75" />
-    <TypeIdentifier>
-      <HashCode>AAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAQA=</HashCode>
-      <FileName>Views\CollectionNavigation\DefaultCollectionNavigatorMatcher.cs</FileName>
-    </TypeIdentifier>
-    <Lollipop Position="0.2" />
-  </Class>
-  <Class Name="Terminal.Gui.Views.TableCollectionNavigator" Collapsed="true">
-    <Position X="4.75" Y="6.5" Width="2.25" />
-    <TypeIdentifier>
-      <HashCode>AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAEAAAAIAAAAAA=</HashCode>
-      <FileName>Views\CollectionNavigation\TableCollectionNavigator.cs</FileName>
-    </TypeIdentifier>
-  </Class>
-  <Class Name="Terminal.Gui.Views.ListView" Collapsed="true">
-    <Position X="0.5" Y="4.25" Width="1.5" />
-    <TypeIdentifier>
-      <HashCode>AAE+ASAkEnAAABAAKGAggYAZJAIAABEAcBAaAwAQIAA=</HashCode>
-      <FileName>Views\ListView.cs</FileName>
-    </TypeIdentifier>
-    <ShowAsAssociation>
-      <Property Name="KeystrokeNavigator" />
-    </ShowAsAssociation>
-    <Lollipop Position="0.2" />
-  </Class>
-  <Class Name="Terminal.Gui.Views.TableView" Collapsed="true" BaseTypeListCollapsed="true">
-    <Position X="0.5" Y="6.5" Width="1.5" />
-    <TypeIdentifier>
-      <HashCode>QwUeAxwgICIAcABIABeR0oBAkhoFGGOBDABgAN3oPEI=</HashCode>
-      <FileName>Views\TableView\TableView.cs</FileName>
-    </TypeIdentifier>
-    <Lollipop Position="0.2" />
-  </Class>
-  <Class Name="Terminal.Gui.Views.TreeView" Collapsed="true">
-    <Position X="0.5" Y="3" Width="1.5" />
-    <TypeIdentifier>
-      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAA=</HashCode>
-      <FileName>Views\TreeView\TreeView.cs</FileName>
-    </TypeIdentifier>
-    <Lollipop Position="0.2" />
-  </Class>
-  <Class Name="Terminal.Gui.Views.TreeView&lt;T&gt;" Collapsed="true">
-    <Position X="0.5" Y="2" Width="1.5" />
-    <TypeIdentifier>
-      <HashCode>UwAGySBgBSBGMAQgIiCaBDUItJIBSAWwRMQOSgQCwJI=</HashCode>
-      <FileName>Views\TreeView\TreeView.cs</FileName>
-    </TypeIdentifier>
-    <ShowAsAssociation>
-      <Property Name="KeystrokeNavigator" />
-    </ShowAsAssociation>
-    <Lollipop Position="0.2" />
-  </Class>
-  <Class Name="Terminal.Gui.Views.FileDialog" Collapsed="true">
-    <Position X="0.5" Y="5.5" Width="1.75" />
-    <Compartments>
-      <Compartment Name="Nested Types" Collapsed="false" />
-    </Compartments>
-    <TypeIdentifier>
-      <HashCode>iIY4LQFUHDKVIHIESBoigQcFT6GxhBDABGJItBQAwAQ=</HashCode>
-      <FileName>Views\FileDialogs\FileDialog.cs</FileName>
-    </TypeIdentifier>
-    <Lollipop Position="0.2" />
-  </Class>
-  <Class Name="Terminal.Gui.Views.FileDialogCollectionNavigator" Collapsed="true">
-    <Position X="4.75" Y="5.5" Width="2.25" />
-    <TypeIdentifier>
-      <HashCode>AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAA=</HashCode>
-      <FileName>Views\FileDialogs\FileDialogCollectionNavigator.cs</FileName>
-    </TypeIdentifier>
-  </Class>
-  <Interface Name="Terminal.Gui.Views.ICollectionNavigatorMatcher" Collapsed="true">
-    <Position X="9.5" Y="1.5" Width="2.75" />
-    <TypeIdentifier>
-      <HashCode>AAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAA=</HashCode>
-      <FileName>Views\CollectionNavigation\ICollectionNavigatorMatcher.cs</FileName>
-    </TypeIdentifier>
-  </Interface>
-  <Interface Name="Terminal.Gui.Views.IListCollectionNavigator" Collapsed="true">
-    <Position X="3.75" Y="2.25" Width="2" />
-    <TypeIdentifier>
-      <HashCode>AAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
-      <FileName>Views\CollectionNavigation\IListCollectionNavigator.cs</FileName>
-    </TypeIdentifier>
-  </Interface>
-  <Interface Name="Terminal.Gui.Views.ICollectionNavigator" Collapsed="true">
-    <Position X="3.75" Y="1.5" Width="2" />
-    <TypeIdentifier>
-      <HashCode>AAgAAAAAAAAAAAAIAAAAAAAAAAAABAAAAAAAACgAAAA=</HashCode>
-      <FileName>Views\CollectionNavigation\ICollectionNavigator.cs</FileName>
-    </TypeIdentifier>
-  </Interface>
-  <Font Name="Segoe UI" Size="9" />
-</ClassDiagram>

+ 0 - 2
Terminal.Gui/Views/Color/BBar.cs

@@ -1,5 +1,3 @@
-
-
 using ColorHelper;
 using ColorHelper;
 
 
 namespace Terminal.Gui.Views;
 namespace Terminal.Gui.Views;

+ 2 - 5
Terminal.Gui/Views/Color/ColorBar.cs

@@ -1,5 +1,3 @@
-
-
 using ColorHelper;
 using ColorHelper;
 
 
 namespace Terminal.Gui.Views;
 namespace Terminal.Gui.Views;
@@ -15,7 +13,7 @@ internal abstract class ColorBar : View, IColorBar
     /// </summary>
     /// </summary>
     protected ColorBar ()
     protected ColorBar ()
     {
     {
-        Height = Dim.Auto(minimumContentDim: 1);
+        Height = Dim.Auto (minimumContentDim: 1);
         Width = Dim.Fill ();
         Width = Dim.Fill ();
         CanFocus = true;
         CanFocus = true;
 
 
@@ -103,7 +101,7 @@ internal abstract class ColorBar : View, IColorBar
     }
     }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    protected override bool OnDrawingContent ()
+    protected override bool OnDrawingContent (DrawContext? context)
     {
     {
         if (!string.IsNullOrWhiteSpace (Text))
         if (!string.IsNullOrWhiteSpace (Text))
         {
         {
@@ -135,7 +133,6 @@ internal abstract class ColorBar : View, IColorBar
 
 
             mouseEvent.Handled = true;
             mouseEvent.Handled = true;
             SetFocus ();
             SetFocus ();
-
         }
         }
 
 
         return mouseEvent.Handled;
         return mouseEvent.Handled;

+ 28 - 45
Terminal.Gui/Views/Color/ColorModelStrategy.cs

@@ -1,5 +1,3 @@
-
-
 using ColorHelper;
 using ColorHelper;
 using ColorConverter = ColorHelper.ColorConverter;
 using ColorConverter = ColorHelper.ColorConverter;
 
 
@@ -7,39 +5,23 @@ namespace Terminal.Gui.Views;
 
 
 internal class ColorModelStrategy
 internal class ColorModelStrategy
 {
 {
-    public IEnumerable<ColorBar> CreateBars (ColorModel model)
-    {
-        switch (model)
+    public IEnumerable<ColorBar> CreateBars (ColorModel model) =>
+        model switch
         {
         {
-            case ColorModel.RGB:
-                return CreateRgbBars ();
-            case ColorModel.HSV:
-                return CreateHsvBars ();
-            case ColorModel.HSL:
-                return CreateHslBars ();
-            default:
-                throw new ArgumentOutOfRangeException (nameof (model), model, null);
-        }
-    }
+            ColorModel.RGB => CreateRgbBars (),
+            ColorModel.HSV => CreateHsvBars (),
+            ColorModel.HSL => CreateHslBars (),
+            _ => throw new ArgumentOutOfRangeException (nameof (model), model, null)
+        };
 
 
-    public Color GetColorFromBars (IList<IColorBar> bars, ColorModel model)
-    {
-        switch (model)
+    public Color GetColorFromBars (IList<IColorBar> bars, ColorModel model) =>
+        model switch
         {
         {
-            case ColorModel.RGB:
-                return ToColor (new ((byte)bars [0].Value, (byte)bars [1].Value, (byte)bars [2].Value));
-            case ColorModel.HSV:
-                return ToColor (
-                                ColorConverter.HsvToRgb (new (bars [0].Value, (byte)bars [1].Value, (byte)bars [2].Value))
-                               );
-            case ColorModel.HSL:
-                return ToColor (
-                                ColorConverter.HslToRgb (new (bars [0].Value, (byte)bars [1].Value, (byte)bars [2].Value))
-                               );
-            default:
-                throw new ArgumentOutOfRangeException (nameof (model), model, null);
-        }
-    }
+            ColorModel.RGB => ToColor (new ((byte)bars [0].Value, (byte)bars [1].Value, (byte)bars [2].Value)),
+            ColorModel.HSV => ToColor (ColorConverter.HsvToRgb (new (bars [0].Value, (byte)bars [1].Value, (byte)bars [2].Value))),
+            ColorModel.HSL => ToColor (ColorConverter.HslToRgb (new (bars [0].Value, (byte)bars [1].Value, (byte)bars [2].Value))),
+            _ => throw new ArgumentOutOfRangeException (nameof (model), model, null)
+        };
 
 
     public void SetBarsToColor (IList<IColorBar> bars, Color newValue, ColorModel model)
     public void SetBarsToColor (IList<IColorBar> bars, Color newValue, ColorModel model)
     {
     {
@@ -47,6 +29,7 @@ internal class ColorModelStrategy
         {
         {
             return;
             return;
         }
         }
+
         switch (model)
         switch (model)
         {
         {
             case ColorModel.RGB:
             case ColorModel.RGB:
@@ -75,21 +58,21 @@ internal class ColorModelStrategy
         }
         }
     }
     }
 
 
-    private IEnumerable<ColorBar> CreateHslBars ()
+    private static IEnumerable<ColorBar> CreateHslBars ()
     {
     {
-        var h = new HueBar
+        HueBar h = new ()
         {
         {
             Text = "H:"
             Text = "H:"
         };
         };
 
 
         yield return h;
         yield return h;
 
 
-        var s = new SaturationBar
+        SaturationBar s = new ()
         {
         {
             Text = "S:"
             Text = "S:"
         };
         };
 
 
-        var l = new LightnessBar
+        LightnessBar l = new ()
         {
         {
             Text = "L:"
             Text = "L:"
         };
         };
@@ -104,21 +87,21 @@ internal class ColorModelStrategy
         yield return l;
         yield return l;
     }
     }
 
 
-    private IEnumerable<ColorBar> CreateHsvBars ()
+    private static IEnumerable<ColorBar> CreateHsvBars ()
     {
     {
-        var h = new HueBar
+        HueBar h = new ()
         {
         {
             Text = "H:"
             Text = "H:"
         };
         };
 
 
         yield return h;
         yield return h;
 
 
-        var s = new SaturationBar
+        SaturationBar s = new ()
         {
         {
             Text = "S:"
             Text = "S:"
         };
         };
 
 
-        var v = new ValueBar
+        ValueBar v = new ()
         {
         {
             Text = "V:"
             Text = "V:"
         };
         };
@@ -133,19 +116,19 @@ internal class ColorModelStrategy
         yield return v;
         yield return v;
     }
     }
 
 
-    private IEnumerable<ColorBar> CreateRgbBars ()
+    private static IEnumerable<ColorBar> CreateRgbBars ()
     {
     {
-        var r = new RBar
+        RBar r = new ()
         {
         {
             Text = "R:"
             Text = "R:"
         };
         };
 
 
-        var g = new GBar
+        GBar g = new ()
         {
         {
             Text = "G:"
             Text = "G:"
         };
         };
 
 
-        var b = new BBar
+        BBar b = new ()
         {
         {
             Text = "B:"
             Text = "B:"
         };
         };
@@ -163,5 +146,5 @@ internal class ColorModelStrategy
         yield return b;
         yield return b;
     }
     }
 
 
-    private Color ToColor (RGB rgb) { return new (rgb.R, rgb.G, rgb.B); }
+    private static Color ToColor (RGB rgb) { return new (rgb.R, rgb.G, rgb.B); }
 }
 }

+ 1 - 1
Terminal.Gui/Views/Color/ColorPicker.16.cs

@@ -132,7 +132,7 @@ public class ColorPicker16 : View
     }
     }
 
 
     ///<inheritdoc/>
     ///<inheritdoc/>
-    protected override bool OnDrawingContent ()
+    protected override bool OnDrawingContent (DrawContext? context)
     {
     {
         SetAttribute (HasFocus ? GetAttributeForRole (VisualRole.Focus) : GetAttributeForRole (VisualRole.Normal));
         SetAttribute (HasFocus ? GetAttributeForRole (VisualRole.Focus) : GetAttributeForRole (VisualRole.Normal));
         var colorIndex = 0;
         var colorIndex = 0;

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

@@ -21,14 +21,14 @@ public partial class ColorPicker
         var d = new Dialog
         var d = new Dialog
         {
         {
             Title = title,
             Title = title,
-            Width = Application.Force16Colors ? 37 : Dim.Auto (DimAutoStyle.Auto, Dim.Percent (80), Dim.Percent (90)),
+            Width = app.Driver!.Force16Colors ? 37 : Dim.Auto (DimAutoStyle.Auto, Dim.Percent (80), Dim.Percent (90)),
             Height = 20
             Height = 20
         };
         };
 
 
         var btnOk = new Button
         var btnOk = new Button
         {
         {
             X = Pos.Center () - 5,
             X = Pos.Center () - 5,
-            Y = Application.Force16Colors ? 6 : 4,
+            Y = app.Driver!.Force16Colors ? 6 : 4,
             Text = "Ok",
             Text = "Ok",
             Width = Dim.Auto (),
             Width = Dim.Auto (),
             IsDefault = true
             IsDefault = true
@@ -63,7 +63,7 @@ public partial class ColorPicker
 
 
         View cpForeground;
         View cpForeground;
 
 
-        if (Application.Force16Colors)
+        if (app.Driver!.Force16Colors)
         {
         {
             cpForeground = new ColorPicker16
             cpForeground = new ColorPicker16
             {
             {
@@ -88,7 +88,7 @@ public partial class ColorPicker
 
 
         View cpBackground;
         View cpBackground;
 
 
-        if (Application.Force16Colors)
+        if (app.Driver!.Force16Colors)
         {
         {
             cpBackground = new ColorPicker16
             cpBackground = new ColorPicker16
             {
             {
@@ -117,8 +117,8 @@ public partial class ColorPicker
 
 
         app.Run (d);
         app.Run (d);
         d.Dispose ();
         d.Dispose ();
-        Color newForeColor = Application.Force16Colors ? ((ColorPicker16)cpForeground).SelectedColor : ((ColorPicker)cpForeground).SelectedColor;
-        Color newBackColor = Application.Force16Colors ? ((ColorPicker16)cpBackground).SelectedColor : ((ColorPicker)cpBackground).SelectedColor;
+        Color newForeColor = app.Driver!.Force16Colors ? ((ColorPicker16)cpForeground).SelectedColor : ((ColorPicker)cpForeground).SelectedColor;
+        Color newBackColor = app.Driver!.Force16Colors ? ((ColorPicker16)cpBackground).SelectedColor : ((ColorPicker)cpBackground).SelectedColor;
         newAttribute = new (newForeColor, newBackColor);
         newAttribute = new (newForeColor, newBackColor);
         app.Dispose ();
         app.Dispose ();
         return accept;
         return accept;

+ 0 - 3
Terminal.Gui/Views/Color/ColorPicker.Style.cs

@@ -1,6 +1,3 @@
-
-
-
 namespace Terminal.Gui.Views;
 namespace Terminal.Gui.Views;
 
 
 /// <summary>
 /// <summary>

+ 1 - 1
Terminal.Gui/Views/Color/ColorPicker.cs

@@ -99,7 +99,7 @@ public partial class ColorPicker : View, IDesignable
     public event EventHandler<ResultEventArgs<Color>>? ColorChanged;
     public event EventHandler<ResultEventArgs<Color>>? ColorChanged;
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    protected override bool OnDrawingContent ()
+    protected override bool OnDrawingContent (DrawContext? context)
     {
     {
         Attribute normal = GetAttributeForRole (VisualRole.Normal);
         Attribute normal = GetAttributeForRole (VisualRole.Normal);
         SetAttribute (new (SelectedColor, normal.Background, Enabled ? TextStyle.None : TextStyle.Faint));
         SetAttribute (new (SelectedColor, normal.Background, Enabled ? TextStyle.None : TextStyle.Faint));

+ 0 - 2
Terminal.Gui/Views/Color/GBar.cs

@@ -1,5 +1,3 @@
-
-
 using ColorHelper;
 using ColorHelper;
 
 
 namespace Terminal.Gui.Views;
 namespace Terminal.Gui.Views;

+ 1 - 2
Terminal.Gui/Views/Color/IColorBar.cs

@@ -1,5 +1,4 @@
-#nullable disable
-namespace Terminal.Gui.Views;
+namespace Terminal.Gui.Views;
 
 
 internal interface IColorBar
 internal interface IColorBar
 {
 {

+ 0 - 2
Terminal.Gui/Views/Color/LightnessBar.cs

@@ -1,5 +1,3 @@
-
-
 using ColorHelper;
 using ColorHelper;
 using ColorConverter = ColorHelper.ColorConverter;
 using ColorConverter = ColorHelper.ColorConverter;
 
 

+ 0 - 2
Terminal.Gui/Views/Color/RBar.cs

@@ -1,5 +1,3 @@
-
-
 using ColorHelper;
 using ColorHelper;
 
 
 namespace Terminal.Gui.Views;
 namespace Terminal.Gui.Views;

+ 0 - 2
Terminal.Gui/Views/Color/SaturationBar.cs

@@ -1,5 +1,3 @@
-
-
 using ColorHelper;
 using ColorHelper;
 using ColorConverter = ColorHelper.ColorConverter;
 using ColorConverter = ColorHelper.ColorConverter;
 
 

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

@@ -287,7 +287,7 @@ public class ComboBox : View, IDesignable
     public virtual void OnCollapsed () { Collapsed?.Invoke (this, EventArgs.Empty); }
     public virtual void OnCollapsed () { Collapsed?.Invoke (this, EventArgs.Empty); }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    protected override bool OnDrawingContent ()
+    protected override bool OnDrawingContent (DrawContext context)
     {
     {
 
 
         if (!_autoHide)
         if (!_autoHide)
@@ -881,7 +881,7 @@ public class ComboBox : View, IDesignable
             return res;
             return res;
         }
         }
 
 
-        protected override bool OnDrawingContent ()
+        protected override bool OnDrawingContent (DrawContext context)
         {
         {
             Attribute current = GetAttributeForRole (VisualRole.Focus);
             Attribute current = GetAttributeForRole (VisualRole.Focus);
             SetAttribute (current);
             SetAttribute (current);

+ 0 - 168
Terminal.Gui/Views/FileDialogs/FileDialog.cd

@@ -1,168 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<ClassDiagram MajorVersion="1" MinorVersion="1">
-  <Class Name="Terminal.Gui.FileServices.DefaultSearchMatcher" Collapsed="true" BaseTypeListCollapsed="true">
-    <Position X="9.25" Y="6" Width="2" />
-    <TypeIdentifier>
-      <HashCode>AAACAAAAAAgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
-      <FileName>FileServices\DefaultSearchMatcher.cs</FileName>
-    </TypeIdentifier>
-    <Lollipop Position="0.2" />
-  </Class>
-  <Class Name="Terminal.Gui.FileServices.FileSystemInfoStats">
-    <Position X="14" Y="0.5" Width="2.5" />
-    <TypeIdentifier>
-      <HashCode>ABAIQAIIIAAAAAACQAAAAIQAAAQAAIAAAQAAAAAIAAI=</HashCode>
-      <FileName>FileServices\FileSystemInfoStats.cs</FileName>
-    </TypeIdentifier>
-  </Class>
-  <Class Name="Terminal.Gui.Views.FileDialog">
-    <Position X="0.5" Y="0.5" Width="2.75" />
-    <Compartments>
-      <Compartment Name="Fields" Collapsed="true" />
-      <Compartment Name="Methods" Collapsed="true" />
-      <Compartment Name="Nested Types" Collapsed="false" />
-    </Compartments>
-    <NestedTypes>
-      <Class Name="Terminal.Gui.Views.FileDialog.SearchState" Collapsed="true">
-        <TypeIdentifier>
-          <NewMemberFileName>Views\FileDialogs\FileDialog.cs</NewMemberFileName>
-        </TypeIdentifier>
-      </Class>
-    </NestedTypes>
-    <TypeIdentifier>
-      <HashCode>iIY4LQFUHDKVIHIESBoigQcFT6GxhBDABGJItBQAwAQ=</HashCode>
-      <FileName>Views\FileDialogs\FileDialog.cs</FileName>
-    </TypeIdentifier>
-    <Lollipop Position="0.2" />
-  </Class>
-  <Class Name="Terminal.Gui.Views.FileDialogStyle">
-    <Position X="3.5" Y="0.5" Width="2.75" />
-    <TypeIdentifier>
-      <HashCode>GgBAAAFHAAAAuAAAAAAAEAQQBYAAKREAAAAYQCCAAAA=</HashCode>
-      <FileName>Views\FileDialogs\FileDialogStyle.cs</FileName>
-    </TypeIdentifier>
-  </Class>
-  <Class Name="Terminal.Gui.Views.FilesSelectedEventArgs">
-    <Position X="6.5" Y="0.5" Width="2" />
-    <Compartments>
-      <Compartment Name="Methods" Collapsed="true" />
-    </Compartments>
-    <TypeIdentifier>
-      <HashCode>AAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAA=</HashCode>
-      <FileName>Views\FileDialogs\FilesSelectedEventArgs.cs</FileName>
-    </TypeIdentifier>
-  </Class>
-  <Class Name="Terminal.Gui.Views.FileDialogHistory">
-    <Position X="9.25" Y="0.5" Width="1.5" />
-    <TypeIdentifier>
-      <HashCode>AQABAgEAAAAAAAAAIACAAAAAAAAAAQAAAAAAAAAADAI=</HashCode>
-      <FileName>Views\FileDialogs\FileDialogHistory.cs</FileName>
-    </TypeIdentifier>
-  </Class>
-  <Class Name="Terminal.Gui.Views.FileDialogState">
-    <Position X="11.5" Y="0.5" Width="1.5" />
-    <TypeIdentifier>
-      <HashCode>AABAABAAAAAAAAIAAAAEQAAAAAAAQAAAAgAAAAAAAAI=</HashCode>
-      <FileName>Views\FileDialogs\FileDialogState.cs</FileName>
-    </TypeIdentifier>
-    <ShowAsCollectionAssociation>
-      <Property Name="Children" />
-    </ShowAsCollectionAssociation>
-  </Class>
-  <Class Name="Terminal.Gui.Views.AllowedType" BaseTypeListCollapsed="true">
-    <Position X="13.25" Y="5.75" Width="1.5" />
-    <TypeIdentifier>
-      <HashCode>AAAAAAAAAAAgAAAEAAAAAAAAAAAAAAAAAAgAAAAABAA=</HashCode>
-      <FileName>Views\FileDialogs\AllowedType.cs</FileName>
-    </TypeIdentifier>
-    <Lollipop Position="0.2" />
-  </Class>
-  <Class Name="Terminal.Gui.Views.AllowedTypeAny" BaseTypeListCollapsed="true">
-    <Position X="11.75" Y="5.75" Width="1.5" />
-    <TypeIdentifier>
-      <HashCode>AAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAABAA=</HashCode>
-      <FileName>Views\FileDialogs\AllowedType.cs</FileName>
-    </TypeIdentifier>
-    <Lollipop Position="0.2" />
-  </Class>
-  <Class Name="Terminal.Gui.Text.NerdFonts">
-    <Position X="9.25" Y="6.75" Width="2" />
-    <Compartments>
-      <Compartment Name="Fields" Collapsed="true" />
-    </Compartments>
-    <TypeIdentifier>
-      <HashCode>AIACAAABQAAAAAAAAAAACAAAIACAAAAAAAIAAAAAAAA=</HashCode>
-      <FileName>Text\NerdFonts.cs</FileName>
-    </TypeIdentifier>
-  </Class>
-  <Class Name="Terminal.Gui.FileServices.FileSystemTreeBuilder" BaseTypeListCollapsed="true">
-    <Position X="0.5" Y="6.75" Width="1.75" />
-    <TypeIdentifier>
-      <HashCode>EAAAAAAAAAAAAAAAAAAABAAwAAAAQAAAAABAAAAACAA=</HashCode>
-      <FileName>FileServices\FileSystemTreeBuilder.cs</FileName>
-    </TypeIdentifier>
-    <Lollipop Position="0.2" />
-  </Class>
-  <Class Name="Terminal.Gui.Views.DefaultFileOperations" BaseTypeListCollapsed="true">
-    <Position X="7" Y="6" Width="2" />
-    <TypeIdentifier>
-      <HashCode>AAAAAAAAAAAAACAAAAAAACAAAAEAAAAAAAAAAAAAgAA=</HashCode>
-      <FileName>Views\FileDialogs\DefaultFileOperations.cs</FileName>
-    </TypeIdentifier>
-    <Lollipop Position="0.2" />
-  </Class>
-  <Class Name="Terminal.Gui.FileServices.FileSystemColorProvider" Collapsed="true">
-    <Position X="7" Y="8.5" Width="2" />
-    <TypeIdentifier>
-      <HashCode>AgAAAAAAAEAAAAAAAAAAAAEAAAAAAACAAAAAAAAAAAA=</HashCode>
-      <FileName>FileServices\FileSystemColorProvider.cs</FileName>
-    </TypeIdentifier>
-  </Class>
-  <Class Name="Terminal.Gui.FileServices.FileSystemIconProvider" Collapsed="true">
-    <Position X="7" Y="7.75" Width="2" />
-    <TypeIdentifier>
-      <HashCode>ABAAAAAAAACAQAAAAAAAAEAgAAAAAQAAAAAAAAAAAiA=</HashCode>
-      <FileName>FileServices\FileSystemIconProvider.cs</FileName>
-    </TypeIdentifier>
-  </Class>
-  <Class Name="Terminal.Gui.Views.FileDialogTableSource">
-    <Position X="2.5" Y="8" Width="2.5" />
-    <Compartments>
-      <Compartment Name="Fields" Collapsed="true" />
-    </Compartments>
-    <TypeIdentifier>
-      <HashCode>AQAAAAAAIAACAEAACAAAAAACAAAEAAAEAAAAgAgBBAA=</HashCode>
-      <FileName>Views\FileDialogs\FileDialogTableSource.cs</FileName>
-    </TypeIdentifier>
-    <Lollipop Position="0.2" />
-  </Class>
-  <Interface Name="Terminal.Gui.FileServices.IFileOperations">
-    <Position X="7" Y="4.25" Width="1.5" />
-    <TypeIdentifier>
-      <HashCode>AAAAAAAAAAAAACAAAAAAAAAAAAEAAAAAAAAAAAAAgAA=</HashCode>
-      <FileName>FileServices\IFileOperations.cs</FileName>
-    </TypeIdentifier>
-  </Interface>
-  <Interface Name="Terminal.Gui.FileServices.ISearchMatcher">
-    <Position X="9.25" Y="4.25" Width="1.5" />
-    <TypeIdentifier>
-      <HashCode>AAACAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
-      <FileName>FileServices\ISearchMatcher.cs</FileName>
-    </TypeIdentifier>
-  </Interface>
-  <Interface Name="Terminal.Gui.Views.IAllowedType" Collapsed="true">
-    <Position X="12" Y="4.25" Width="1.5" />
-    <TypeIdentifier>
-      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAA=</HashCode>
-      <FileName>Views\FileDialogs\AllowedType.cs</FileName>
-    </TypeIdentifier>
-  </Interface>
-  <Enum Name="Terminal.Gui.Views.OpenMode">
-    <Position X="7.5" Y="2.5" Width="1.5" />
-    <TypeIdentifier>
-      <HashCode>AAAAABAAAAAAACAAAAAAAAAAAAAEAAAAAAAAAAAAAAA=</HashCode>
-      <FileName>Views\FileDialogs\OpenMode.cs</FileName>
-    </TypeIdentifier>
-  </Enum>
-  <Font Name="Segoe UI" Size="9" />
-</ClassDiagram>

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

@@ -409,7 +409,7 @@ public class FileDialog : Dialog, IDesignable
     }
     }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    protected override bool OnDrawingContent ()
+    protected override bool OnDrawingContent (DrawContext? context)
     {
     {
         if (!string.IsNullOrWhiteSpace (_feedback))
         if (!string.IsNullOrWhiteSpace (_feedback))
         {
         {

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

@@ -198,7 +198,7 @@ public class GraphView : View, IDesignable
     }
     }
 
 
     ///<inheritdoc/>
     ///<inheritdoc/>
-    protected override bool OnDrawingContent ()
+    protected override bool OnDrawingContent (DrawContext? context)
     {
     {
         if (CellSize.X == 0 || CellSize.Y == 0)
         if (CellSize.X == 0 || CellSize.Y == 0)
         {
         {

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

@@ -431,7 +431,7 @@ public class HexView : View, IDesignable
     }
     }
 
 
     ///<inheritdoc/>
     ///<inheritdoc/>
-    protected override bool OnDrawingContent ()
+    protected override bool OnDrawingContent (DrawContext? context)
     {
     {
         if (Source is null)
         if (Source is null)
         {
         {

部分文件因文件數量過多而無法顯示