Browse Source

Merge branch 'v2_develop' of tig:tig/Terminal.Gui into v2_develop

Tig 1 month ago
parent
commit
d1a709af1e
70 changed files with 3075 additions and 1568 deletions
  1. 2 45
      .cursorrules
  2. 123 24
      .github/ISSUE_TEMPLATE/bug_report.md
  3. 3 291
      .github/copilot-instructions.md
  4. 0 47
      .github/workflows/build-release.yml
  5. 76 0
      .github/workflows/build.yml
  6. 13 6
      .github/workflows/integration-tests.yml
  7. 20 10
      .github/workflows/unit-tests.yml
  8. 10 175
      AGENTS.md
  9. 346 124
      CONTRIBUTING.md
  10. 1 1
      README.md
  11. 16 6
      Terminal.Gui/App/Application.Driver.cs
  12. 1 1
      Terminal.Gui/App/Application.Keyboard.cs
  13. 16 29
      Terminal.Gui/App/Application.Lifecycle.cs
  14. 50 252
      Terminal.Gui/App/Application.Mouse.cs
  15. 5 1
      Terminal.Gui/App/Application.Navigation.cs
  16. 5 1
      Terminal.Gui/App/Application.Popover.cs
  17. 11 88
      Terminal.Gui/App/Application.Run.cs
  18. 8 29
      Terminal.Gui/App/Application.Screen.cs
  19. 6 14
      Terminal.Gui/App/Application.Toplevel.cs
  20. 10 5
      Terminal.Gui/App/Application.cs
  21. 187 40
      Terminal.Gui/App/ApplicationImpl.cs
  22. 98 58
      Terminal.Gui/App/IApplication.cs
  23. 2 2
      Terminal.Gui/App/Keyboard/KeyboardImpl.cs
  24. 1 1
      Terminal.Gui/App/MainLoop/ApplicationMainLoop.cs
  25. 79 0
      Terminal.Gui/App/Mouse/IMouse.cs
  26. 8 0
      Terminal.Gui/App/Mouse/IMouseGrabHandler.cs
  27. 41 0
      Terminal.Gui/App/Mouse/MouseGrabHandler.cs
  28. 393 0
      Terminal.Gui/App/Mouse/MouseImpl.cs
  29. 5 0
      Terminal.Gui/Drivers/DotNetDriver/NetInput.cs
  30. 1 3
      Terminal.Gui/Drivers/IConsoleDriver.cs
  31. 34 68
      Terminal.Gui/README.md
  32. 9 9
      Terminal.Gui/ViewBase/Adornment/Border.Arrangment.cs
  33. 2 2
      Terminal.Gui/ViewBase/Adornment/Border.cs
  34. 21 10
      Terminal.Gui/ViewBase/View.Drawing.cs
  35. 6 6
      Terminal.Gui/ViewBase/View.Mouse.cs
  36. 2 2
      Terminal.Gui/ViewBase/View.cs
  37. 1 1
      Terminal.Gui/Views/Autocomplete/PopupAutocomplete.cs
  38. 2 2
      Terminal.Gui/Views/ComboBox.cs
  39. 30 0
      Terminal.Gui/Views/Dialog.cs
  40. 4 4
      Terminal.Gui/Views/Menuv1/Menu.cs
  41. 33 33
      Terminal.Gui/Views/Menuv1/MenuBar.cs
  42. 4 4
      Terminal.Gui/Views/ScrollBar/ScrollSlider.cs
  43. 2 2
      Terminal.Gui/Views/Slider/Slider.cs
  44. 6 6
      Terminal.Gui/Views/TextInput/TextField.cs
  45. 6 6
      Terminal.Gui/Views/TextInput/TextView.cs
  46. 3 2
      Terminal.sln
  47. 8 3
      Tests/StressTests/ApplicationStressTests.cs
  48. 7 4
      Tests/TerminalGuiFluentTesting/GuiTestContext.cs
  49. 2 2
      Tests/TerminalGuiFluentTesting/With.cs
  50. 48 0
      Tests/UnitTests/Application/ApplicationImplTests.cs
  51. 5 6
      Tests/UnitTests/Application/ApplicationTests.cs
  52. 98 0
      Tests/UnitTests/Application/MainLoopTests.cs
  53. 31 31
      Tests/UnitTests/Application/Mouse/ApplicationMouseTests.cs
  54. 9 13
      Tests/UnitTests/Dialogs/WizardTests.cs
  55. 2 2
      Tests/UnitTests/View/Adornment/BorderTests.cs
  56. 1 1
      Tests/UnitTests/View/Adornment/ShadowStyleTests.cs
  57. 3 3
      Tests/UnitTests/View/Draw/ClearViewportTests.cs
  58. 8 8
      Tests/UnitTests/View/Mouse/MouseTests.cs
  59. 6 6
      Tests/UnitTests/Views/LabelTests.cs
  60. 10 11
      Tests/UnitTests/Views/Menuv1/MenuBarv1Tests.cs
  61. 2 2
      Tests/UnitTests/Views/TextViewTests.cs
  62. 28 28
      Tests/UnitTests/Views/ToplevelTests.cs
  63. 33 33
      Tests/UnitTestsParallelizable/Application/KeyboardTests.cs
  64. 444 0
      Tests/UnitTestsParallelizable/Application/MouseInterfaceTests.cs
  65. 125 0
      Tests/UnitTestsParallelizable/Application/MouseTests.cs
  66. 1 1
      Tests/UnitTestsParallelizable/TestSetup.cs
  67. 498 0
      Tests/UnitTestsParallelizable/View/Mouse/MouseEventRoutingTests.cs
  68. 4 4
      docfx/docs/multitasking.md
  69. BIN
      local_packages/Terminal.Gui.2.0.0.nupkg
  70. BIN
      local_packages/Terminal.Gui.2.0.0.snupkg

+ 2 - 45
.cursorrules

@@ -1,48 +1,5 @@
 # Terminal.Gui - Cursor AI Rules
 # Terminal.Gui - Cursor AI Rules
 
 
-This project uses comprehensive AI agent instructions. See:
-- `.github/copilot-instructions.md` - Complete onboarding guide (primary reference)
-- `AGENTS.md` - General AI agent guidelines
+> **📘 Source of Truth: [CONTRIBUTING.md](CONTRIBUTING.md)**
 
 
-## Quick Reference
-
-### Project Type
-- .NET 8.0 cross-platform console UI toolkit
-- 496 source files in core library
-- GitFlow branching (v2_develop = default)
-
-### Essential Commands
-
-```bash
-# Always run from repo root in this order:
-dotnet restore                                              # First! (~15-20s)
-dotnet build --configuration Debug --no-restore            # ~50s, expect ~326 warnings
-dotnet test Tests/UnitTestsParallelizable --no-build       # Preferred test suite
-dotnet run --project Examples/UICatalog/UICatalog.csproj   # Demo app
-```
-
-### Code Style (Enforced)
-- **DO**: Use explicit types (avoid `var`), target-typed `new()`, 4-space indent
-- **DO**: Format only files you modify (ReSharper/Rider `Ctrl-E-C` or `Ctrl-K-D`)
-- **DO**: Follow `.editorconfig` and `Terminal.sln.DotSettings`
-- **DON'T**: Add new linters, modify unrelated code, decrease test coverage
-
-### Testing Rules
-- Add new tests to `Tests/UnitTestsParallelizable/` (preferred)
-- Avoid `Application.Init` and static dependencies in tests
-- Don't use `[AutoInitShutdown]` attribute (legacy)
-- Maintain 70%+ code coverage on new code
-
-### API Documentation (Required)
-- All public APIs need XML docs (`<summary>`, `<remarks>`, `<example>`)
-- Use `<see cref=""/>` for cross-references
-- Complex topics → `docfx/docs/*.md`
-
-### Common Issues
-- ~326 build warnings are normal (nullable refs, etc.)
-- Tests can take 5-10 minutes
-- Run `dotnet restore` before any build
-- Read `.github/copilot-instructions.md` for full troubleshooting
-
----
-**See `.github/copilot-instructions.md` for complete instructions**
+This project uses [CONTRIBUTING.md](CONTRIBUTING.md) as the single source of truth for contribution guidelines. AI agents, including CoPilot and Cursor **MUST** follow the guidelines in [CONTRIBUTING.md](CONTRIBUTING.md)/

+ 123 - 24
.github/ISSUE_TEMPLATE/bug_report.md

@@ -1,41 +1,140 @@
 ---
 ---
 name: Bug report
 name: Bug report
-about: Create a report to help us improve
+about: Create a report to help us improve Terminal.Gui
 title: ''
 title: ''
 labels: bug
 labels: bug
 assignees: ''
 assignees: ''
 
 
 ---
 ---
 
 
-**Describe the bug**
+## Describe the bug
+
 A clear and concise description of what the bug is.
 A clear and concise description of what the bug is.
 
 
-**To Reproduce**
+## To Reproduce
+
 Steps to reproduce the behavior:
 Steps to reproduce the behavior:
-1. Go to '...'
-2. Click on '....'
-3. Scroll down to '....'
-4. See error
 
 
-**Expected behavior**
-A clear and concise description of what you expected to happen.
+1. Run the following code:
+   ```csharp
+   // Paste your minimal reproduction code here
+   ```
+
+2. Expected behavior: (describe what should happen)
+
+3. Actual behavior: (describe what actually happens)
+
+## Environment
+
+Please run the following commands in your terminal and paste the output:
+
+### OS Information
+
+**Windows (PowerShell):**
+```powershell
+"OS: $(Get-CimInstance Win32_OperatingSystem | Select-Object -ExpandProperty Caption) $(Get-CimInstance Win32_OperatingSystem | Select-Object -ExpandProperty Version)"
+```
+
+**macOS/Linux:**
+```bash
+echo "OS: $(uname -s) $(uname -r)"
+```
+
+**Output:**
+```
+(paste output here)
+```
+
+### Terminal Information
+
+**Windows Terminal:**
+```powershell
+"Terminal: Windows Terminal $(Get-AppxPackage -Name Microsoft.WindowsTerminal | Select-Object -ExpandProperty Version)"
+```
+
+**Other terminals:**
+```bash
+echo $TERM
+```
+
+**Output:**
+```
+(paste output here)
+```
+
+### PowerShell Version
+
+```powershell
+$PSVersionTable.PSVersion
+```
+
+**Output:**
+```
+(paste output here)
+```
+
+### .NET Information
+
+```bash
+dotnet --version
+dotnet --info
+```
+
+**Output:**
+```
+(paste output here)
+```
+
+### Terminal.Gui Version
+
+**Option 1 - Run UICatalog (easiest):**
+
+UICatalog displays the Terminal.Gui version in its About box and status bar.
+
+```bash
+dotnet run --project Examples/UICatalog/UICatalog.csproj
+```
+
+**Option 2 - NuGet Package Version:**
+```
+(e.g., 2.0.0-alpha.1, 2.0.0-develop.123, etc.)
+```
+
+**Option 3 - Building from source:**
+```bash
+git rev-parse HEAD
+git describe --tags --always --dirty
+```
+
+**Version:**
+```
+(paste version here)
+```
+
+## Screenshots, GIFs, or Terminal Output
+
+If applicable, add screenshots, animated GIFs, or copy/paste terminal output to help explain your problem.
+
+**Animated GIFs are especially helpful for showing behavior!**
+
+- **Windows**: [ShareX](https://getsharex.com/) (free, captures screen to GIF)
+- **macOS**: [Kap](https://getkap.co/) (free, open source)
+- **Linux**: [Peek](https://github.com/phw/peek) (free)
+
+**For terminal output, use code blocks:**
 
 
-**Screenshots**
-If applicable, add screenshots to help explain your problem.
+```
+(paste terminal output here)
+```
 
 
-**Desktop (please complete the following information):**
- - OS: [e.g. iOS]
- - Browser [e.g. chrome, safari]
- - Version [e.g. 22]
+## Additional context
 
 
-**Smartphone (please complete the following information):**
- - Device: [e.g. iPhone6]
- - OS: [e.g. iOS8.1]
- - Browser [e.g. stock browser, safari]
- - Version [e.g. 22]
+Add any other context about the problem here, such as:
+- Does this happen consistently or intermittently?
+- Did this work in a previous version?
+- Are there any error messages in the console?
+- Terminal configuration or settings that might be relevant?
 
 
-**Additional context**
-Add any other context about the problem here.
+## For Maintainers
 
 
-**Set Project & Milestone**
-If you have access, please don't forget to set the right Project and Milestone.
+**Set Project & Milestone:** If you have access, please don't forget to set the right Project and Milestone.

+ 3 - 291
.github/copilot-instructions.md

@@ -1,293 +1,5 @@
-# Terminal.Gui - Copilot Coding Agent Instructions
+# Terminal.Gui - CoPilot AI Rules
 
 
-This file provides onboarding instructions for GitHub Copilot and other AI coding agents working with Terminal.Gui.
+> **📘 Source of Truth: [CONTRIBUTING.md](CONTRIBUTING.md)**
 
 
-## Project Overview
-
-**Terminal.Gui** is a cross-platform UI toolkit for creating console-based graphical user interfaces in .NET. It's a large codebase (~1,050 C# files, 333MB) providing a comprehensive framework for building interactive console applications with support for keyboard and mouse input, customizable views, and a robust event system.
-
-**Key characteristics:**
-- **Language**: C# (net8.0)
-- **Size**: ~496 source files in core library, ~1,050 total C# files
-- **Platform**: Cross-platform (Windows, macOS, Linux)
-- **Architecture**: Console UI toolkit with driver-based architecture
-- **Version**: v2 (Alpha), v1 (maintenance mode)
-- **Branching**: GitFlow model (v2_develop is default/active development)
-
-## Building and Testing
-
-### Required Tools
-- **.NET SDK**: 8.0.0 (see `global.json`)
-- **Runtime**: .NET 8.x (latest GA)
-- **Optional**: ReSharper/Rider for code formatting
-
-### Build Commands (In Order)
-
-**ALWAYS run these commands from the repository root:**
-
-1. **Restore packages** (required first, ~15-20 seconds):
-   ```bash
-   dotnet restore
-   ```
-
-2. **Build solution** (Debug, ~50 seconds):
-   ```bash
-   dotnet build --configuration Debug --no-restore
-   ```
-   - Expect ~326 warnings (nullable reference warnings, unused variables, etc.) - these are normal
-   - 0 errors expected
-
-3. **Build Release** (for packaging):
-   ```bash
-   dotnet build --configuration Release --no-restore
-   ```
-
-### Test Commands
-
-**Two test projects exist:**
-
-1. **Non-parallel tests** (depend on static state, ~10 min timeout):
-   ```bash
-   dotnet test Tests/UnitTests --no-build --verbosity normal
-   ```
-   - Uses `Application.Init` and static state
-   - Cannot run in parallel
-   - Includes `--blame` flags for crash diagnostics
-
-2. **Parallel tests** (can run concurrently, ~10 min timeout):
-   ```bash
-   dotnet test Tests/UnitTestsParallelizable --no-build --verbosity normal
-   ```
-   - No dependencies on static state
-   - Preferred for new tests
-
-3. **Integration tests**:
-   ```bash
-   dotnet test Tests/IntegrationTests --no-build --verbosity normal
-   ```
-
-**Important**: Tests may take significant time. CI uses blame flags for crash detection:
-```bash
---diag:logs/UnitTests/logs.txt --blame --blame-crash --blame-hang --blame-hang-timeout 60s --blame-crash-collect-always
-```
-
-### Running Examples
-
-**UICatalog** (comprehensive demo app):
-```bash
-dotnet run --project Examples/UICatalog/UICatalog.csproj
-```
-
-## Repository Structure
-
-### Root Directory Files
-- `Terminal.sln` - Main solution file
-- `Terminal.sln.DotSettings` - ReSharper code style settings
-- `.editorconfig` - Code formatting rules (111KB, extensive)
-- `global.json` - .NET SDK version pinning
-- `Directory.Build.props` - Common MSBuild properties
-- `Directory.Packages.props` - Central package version management
-- `GitVersion.yml` - Version numbering configuration
-- `AGENTS.md` - General AI agent instructions (also useful reference)
-- `CONTRIBUTING.md` - Contribution guidelines
-- `README.md` - Project documentation
-
-### Main Directories
-
-**`/Terminal.Gui/`** - Core library (496 C# files):
-- `App/` - Application lifecycle (`Application.cs` static class, `RunState`, `MainLoop`)
-- `Configuration/` - `ConfigurationManager` for settings
-- `Drivers/` - Console driver implementations (`IConsoleDriver`, `NetDriver`, `UnixDriver`, `WindowsDriver`)
-- `Drawing/` - Rendering system (attributes, colors, glyphs)
-- `Input/` - Keyboard and mouse input handling
-- `ViewBase/` - Core `View` class hierarchy and layout
-- `Views/` - Specific View subclasses (Window, Dialog, Button, ListView, etc.)
-- `Text/` - Text manipulation and formatting
-
-**`/Tests/`**:
-- `UnitTests/` - Non-parallel tests (use `Application.Init`, static state)
-- `UnitTestsParallelizable/` - Parallel tests (no static dependencies)
-- `IntegrationTests/` - Integration tests
-- `StressTests/` - Long-running stress tests (scheduled daily)
-- `coverlet.runsettings` - Code coverage configuration
-
-**`/Examples/`**:
-- `UICatalog/` - Comprehensive demo app for manual testing
-- `Example/` - Basic example
-- `NativeAot/`, `SelfContained/` - Deployment examples
-- `ReactiveExample/`, `CommunityToolkitExample/` - Integration examples
-
-**`/docfx/`** - Documentation source:
-- `docs/` - Conceptual documentation (deep dives)
-- `api/` - Generated API docs (gitignored)
-- `docfx.json` - DocFX configuration
-
-**`/Scripts/`** - PowerShell build utilities (requires PowerShell 7.4+)
-
-**`/.github/workflows/`** - CI/CD pipelines:
-- `unit-tests.yml` - Main test workflow (Ubuntu, Windows, macOS)
-- `build-release.yml` - Release build verification
-- `integration-tests.yml` - Integration test workflow
-- `publish.yml` - NuGet package publishing
-- `api-docs.yml` - Documentation building and deployment
-- `codeql-analysis.yml` - Security scanning
-
-## Code Style and Quality
-
-### Formatting
-- **Do NOT add formatting tools** - Use existing `.editorconfig` and `Terminal.sln.DotSettings`
-- Format code with:
-  1. ReSharper/Rider (`Ctrl-E-C`)
-  2. JetBrains CleanupCode CLI tool (free)
-  3. Visual Studio (`Ctrl-K-D`) as fallback
-- **Only format files you modify**
-
-### Code Style Tenets
-1. **Six-Year-Old Reading Level** - Readability over terseness
-2. **Consistency, Consistency, Consistency** - Follow existing patterns ruthlessly
-3. **Don't be Weird** - Follow Microsoft/.NET conventions
-4. **Set and Forget** - Rely on automated tooling
-5. **Documentation is the Spec** - API docs are source of truth
-
-### Coding Conventions
-- Use explicit types (avoid `var` except for basic types like `int`, `string`)
-- Use target-typed `new()`
-- Follow `.editorconfig` settings (e.g., braces on new lines, spaces after keywords)
-- 4-space indentation
-- See `CONTRIBUTING.md` for full guidelines
-
-## Testing Requirements
-
-### Code Coverage
-- **Never decrease code coverage** - PRs must maintain or increase coverage
-- Target: 70%+ coverage for new code
-- CI monitors coverage on each PR
-
-### Test Patterns
-- **Parallelizable tests preferred** - Add new tests to `UnitTestsParallelizable` when possible
-- **Avoid static dependencies** - Don't use `Application.Init`, `ConfigurationManager` in tests
-- **Don't use `[AutoInitShutdown]`** - Legacy pattern, being phased out
-- **Make tests granular** - Each test should cover smallest area possible
-- Follow existing test patterns in respective test projects
-
-### Test Configuration
-- `xunit.runner.json` - xUnit configuration
-- `coverlet.runsettings` - Coverage settings (OpenCover format)
-
-## API Documentation Requirements
-
-**All public APIs MUST have XML documentation:**
-- Clear, concise `<summary>` tags
-- Use `<see cref=""/>` for cross-references
-- Add `<remarks>` for context
-- Include `<example>` for non-obvious usage
-- Complex topics → `docfx/docs/*.md` files
-- Proper English and grammar
-
-## Common Build Issues
-
-### Issue: Build Warnings
-- **Expected**: ~326 warnings (nullable refs, unused vars, xUnit suggestions)
-- **Action**: Don't add new warnings; fix warnings in code you modify
-
-### Issue: Test Timeouts
-- **Expected**: Tests can take 5-10 minutes
-- **Action**: Use appropriate timeout values (60-120 seconds for test commands)
-
-### Issue: Restore Failures
-- **Solution**: Ensure `dotnet restore` completes before building
-- **Note**: Takes 15-20 seconds on first run
-
-### Issue: NativeAot/SelfContained Build
-- **Solution**: Restore these projects explicitly:
-  ```bash
-  dotnet restore ./Examples/NativeAot/NativeAot.csproj -f
-  dotnet restore ./Examples/SelfContained/SelfContained.csproj -f
-  ```
-
-## CI/CD Validation
-
-The following checks run on PRs:
-
-1. **Unit Tests** (`unit-tests.yml`):
-   - Runs on Ubuntu, Windows, macOS
-   - Both parallel and non-parallel test suites
-   - Code coverage collection
-   - 10-minute timeout per job
-
-2. **Build Release** (`build-release.yml`):
-   - Verifies Release configuration builds
-   - Tests NativeAot and SelfContained builds
-   - Packs NuGet package
-
-3. **Integration Tests** (`integration-tests.yml`):
-   - Cross-platform integration testing
-   - 10-minute timeout
-
-4. **CodeQL Analysis** (`codeql-analysis.yml`):
-   - Security vulnerability scanning
-
-To replicate CI locally:
-```bash
-# Full CI sequence:
-dotnet restore
-dotnet build --configuration Debug --no-restore
-dotnet test Tests/UnitTests --no-build --verbosity normal
-dotnet test Tests/UnitTestsParallelizable --no-build --verbosity normal
-dotnet build --configuration Release --no-restore
-```
-
-## Branching and PRs
-
-### GitFlow Model
-- `v2_develop` - Default branch, active development
-- `v2_release` - Stable releases, matches NuGet
-- `v1_develop`, `v1_release` - Legacy v1 (maintenance only)
-
-### PR Requirements
-- **Title**: "Fixes #issue. Terse description"
-- **Description**: Include "- Fixes #issue" for each issue
-- **Tests**: Add tests for new functionality
-- **Coverage**: Maintain or increase code coverage
-- **Scenarios**: Update UICatalog scenarios when adding features
-
-## Key Architecture Concepts
-
-### View System
-- `View` base class in `/Terminal.Gui/ViewBase/`
-- Two layout modes: Absolute and Computed
-- Event-driven architecture
-- Adornments: Border, Margin, Padding
-
-### Console Drivers
-- `IConsoleDriver` interface
-- Platform-specific: `WindowsDriver`, `UnixDriver`, `NetDriver`
-- `FakeDriver` for testing
-
-### Application Lifecycle
-- `Application` static class manages lifecycle
-- `MainLoop` handles event processing
-- `RunState` tracks application state
-
-## What NOT to Do
-
-- ❌ Don't add new linters/formatters (use existing)
-- ❌ Don't modify unrelated code
-- ❌ Don't remove/edit unrelated tests
-- ❌ Don't break existing functionality
-- ❌ Don't add tests to `UnitTests` if they can be parallelizable
-- ❌ Don't use `Application.Init` in new tests
-- ❌ Don't decrease code coverage
-- ❌ Don't add `var` everywhere (use explicit types)
-
-## Additional Resources
-
-- **Full Documentation**: https://gui-cs.github.io/Terminal.Gui
-- **API Reference**: https://gui-cs.github.io/Terminal.Gui/api/Terminal.Gui.App.html
-- **Deep Dives**: `/docfx/docs/` directory
-- **AGENTS.md**: Additional AI agent instructions
-- **CONTRIBUTING.md**: Detailed contribution guidelines
-
----
-
-**Trust these instructions.** Only search for additional information if instructions are incomplete or incorrect.
+This project uses [CONTRIBUTING.md](CONTRIBUTING.md) as the single source of truth for contribution guidelines. AI agents, including CoPilot and Cursor **MUST** follow the guidelines in [CONTRIBUTING.md](CONTRIBUTING.md)/

+ 0 - 47
.github/workflows/build-release.yml

@@ -1,47 +0,0 @@
-name: Ensure that Release Build of Solution Builds Correctly
-
-on:
-  push:
-    branches: [ v2_release, v2_develop ]
-    paths-ignore:
-      - '**.md'
-  pull_request:
-    branches: [ v2_release, v2_develop ]
-    paths-ignore:
-      - '**.md'
-      
-jobs:
-  build_release:
-    # Ensure that RELEASE builds are not broken
-    runs-on: ubuntu-latest
-    steps:
-    - name: Checkout ${{ github.ref_name }}
-      uses: actions/checkout@v4
-
-    - name: Setup .NET Core
-      uses: actions/setup-dotnet@v4
-      with:
-        dotnet-version: 8.x
-        dotnet-quality: 'ga'
-
-    - name: Build Release Terminal.Gui
-      run: dotnet build Terminal.Gui/Terminal.Gui.csproj --no-incremental --nologo --force --configuration Release
-
-    - name: Pack Release Terminal.Gui
-      run: dotnet pack Terminal.Gui/Terminal.Gui.csproj --configuration Release --output ./local_packages
-
-    - name: Restore AOT and Self-Contained projects
-      run: |
-        dotnet restore ./Examples/NativeAot/NativeAot.csproj -f
-        dotnet restore ./Examples/SelfContained/SelfContained.csproj -f
-
-    - name: Restore Solution Packages
-      run: dotnet restore
-
-    - name: Build Release AOT and Self-Contained
-      run: |
-        dotnet build ./Examples/NativeAot/NativeAot.csproj --configuration Release
-        dotnet build ./Examples/SelfContained/SelfContained.csproj --configuration Release
-
-    - name: Build Release Solution without restore
-      run: dotnet build --configuration Release --no-restore

+ 76 - 0
.github/workflows/build.yml

@@ -0,0 +1,76 @@
+name: Build Solution
+
+on:
+  push:
+    branches: [ v2_release, v2_develop ]
+    paths-ignore:
+      - '**.md'
+  pull_request:
+    branches: [ v2_release, v2_develop ]
+    paths-ignore:
+      - '**.md'
+  workflow_call:
+    outputs:
+      artifact-name:
+        description: "Name of the build artifacts"
+        value: ${{ jobs.build.outputs.artifact-name }}
+      
+jobs:
+  build:
+    name: Build Debug & Release
+    runs-on: ubuntu-latest
+    outputs:
+      artifact-name: build-artifacts
+    
+    timeout-minutes: 10
+    steps:
+
+    - name: Checkout code
+      uses: actions/checkout@v4
+
+    - name: Setup .NET Core
+      uses: actions/setup-dotnet@v4
+      with:
+        dotnet-version: 8.x
+        dotnet-quality: 'ga'
+
+    - name: Restore dependencies
+      run: dotnet restore
+
+    # Suppress CS0618 (member is obsolete) and CS0612 (member is obsolete without message)
+    # Using -property: syntax with URL-encoded semicolon (%3B) to avoid shell interpretation issues
+    - name: Build Debug
+      run: dotnet build --configuration Debug --no-restore -property:NoWarn=0618%3B0612
+
+    - name: Build Release Terminal.Gui
+      run: dotnet build Terminal.Gui/Terminal.Gui.csproj --configuration Release --no-incremental --force -property:NoWarn=0618%3B0612
+
+    - name: Pack Release Terminal.Gui
+      run: dotnet pack Terminal.Gui/Terminal.Gui.csproj --configuration Release --output ./local_packages -property:NoWarn=0618%3B0612
+
+    - name: Restore AOT and Self-Contained projects
+      run: |
+        dotnet restore ./Examples/NativeAot/NativeAot.csproj -f
+        dotnet restore ./Examples/SelfContained/SelfContained.csproj -f
+
+    - name: Restore Solution Packages
+      run: dotnet restore
+
+    - name: Build Release AOT and Self-Contained
+      run: |
+        dotnet build ./Examples/NativeAot/NativeAot.csproj --configuration Release -property:NoWarn=0618%3B0612
+        dotnet build ./Examples/SelfContained/SelfContained.csproj --configuration Release -property:NoWarn=0618%3B0612
+
+    - name: Build Release Solution
+      run: dotnet build --configuration Release --no-restore -property:NoWarn=0618%3B0612
+
+    - name: Upload build artifacts
+      uses: actions/upload-artifact@v4
+      with:
+        name: build-artifacts
+        path: |
+          **/bin/Debug/**
+          **/obj/Debug/**
+          **/bin/Release/**
+          **/obj/Release/**
+        retention-days: 1

+ 13 - 6
.github/workflows/integration-tests.yml

@@ -11,9 +11,14 @@ on:
       - '**.md'
       - '**.md'
       
       
 jobs:
 jobs:
-  build_and_test_debug:
+  # Call the build workflow to build the solution once
+  build:
+    uses: ./.github/workflows/build.yml
 
 
+  integration_tests:
+    name: Integration Tests
     runs-on: ${{ matrix.os }}
     runs-on: ${{ matrix.os }}
+    needs: build
     strategy:
     strategy:
       # Turn off fail-fast to let all runners run even if there are errors
       # Turn off fail-fast to let all runners run even if there are errors
       fail-fast: true
       fail-fast: true
@@ -32,12 +37,14 @@ jobs:
         dotnet-version: 8.x
         dotnet-version: 8.x
         dotnet-quality: 'ga'
         dotnet-quality: 'ga'
 
 
-    - name: Install dependencies
-      run: |
-        dotnet restore
+    - name: Download build artifacts
+      uses: actions/download-artifact@v4
+      with:
+        name: build-artifacts
+        path: .
 
 
-    - name: Build IntegrationTests
-      run: dotnet build Tests/IntegrationTests --configuration Debug --no-restore
+    - name: Restore NuGet packages
+      run: dotnet restore
 
 
     - name: Set VSTEST_DUMP_PATH
     - name: Set VSTEST_DUMP_PATH
       shell: bash
       shell: bash

+ 20 - 10
.github/workflows/unit-tests.yml

@@ -11,9 +11,14 @@ on:
       - '**.md'
       - '**.md'
       
       
 jobs:
 jobs:
+  # Call the build workflow to build the solution once
+  build:
+    uses: ./.github/workflows/build.yml
+
   non_parallel_unittests:
   non_parallel_unittests:
     name: Non-Parallel Unit Tests  
     name: Non-Parallel Unit Tests  
     runs-on: ${{ matrix.os }}
     runs-on: ${{ matrix.os }}
+    needs: build
     strategy:
     strategy:
       # Turn off fail-fast to let all runners run even if there are errors
       # Turn off fail-fast to let all runners run even if there are errors
       fail-fast: true
       fail-fast: true
@@ -32,12 +37,14 @@ jobs:
         dotnet-version: 8.x
         dotnet-version: 8.x
         dotnet-quality: 'ga'
         dotnet-quality: 'ga'
 
 
-    - name: Install dependencies
-      run: |
-        dotnet restore
+    - name: Download build artifacts
+      uses: actions/download-artifact@v4
+      with:
+        name: build-artifacts
+        path: .
 
 
-    - name: Build Solution Debug
-      run: dotnet build --configuration Debug --no-restore
+    - name: Restore NuGet packages
+      run: dotnet restore
 
 
 # Test
 # Test
     # Note: The --blame and VSTEST_DUMP_PATH stuff is needed to diagnose the test runner crashing on ubuntu/mac
     # Note: The --blame and VSTEST_DUMP_PATH stuff is needed to diagnose the test runner crashing on ubuntu/mac
@@ -66,6 +73,7 @@ jobs:
   parallel_unittests:
   parallel_unittests:
     name: Parallel Unit Tests  
     name: Parallel Unit Tests  
     runs-on: ${{ matrix.os }}
     runs-on: ${{ matrix.os }}
+    needs: build
     strategy:
     strategy:
       # Turn off fail-fast to let all runners run even if there are errors
       # Turn off fail-fast to let all runners run even if there are errors
       fail-fast: true
       fail-fast: true
@@ -84,12 +92,14 @@ jobs:
         dotnet-version: 8.x
         dotnet-version: 8.x
         dotnet-quality: 'ga'
         dotnet-quality: 'ga'
 
 
-    - name: Install dependencies
-      run: |
-        dotnet restore
+    - name: Download build artifacts
+      uses: actions/download-artifact@v4
+      with:
+        name: build-artifacts
+        path: .
 
 
-    - name: Build Solution Debug
-      run: dotnet build --configuration Debug --no-restore
+    - name: Restore NuGet packages
+      run: dotnet restore
 
 
 # Test
 # Test
     # Note: The --blame and VSTEST_DUMP_PATH stuff is needed to diagnose the test runner crashing on ubuntu/mac
     # Note: The --blame and VSTEST_DUMP_PATH stuff is needed to diagnose the test runner crashing on ubuntu/mac

+ 10 - 175
AGENTS.md

@@ -1,179 +1,14 @@
-# Terminal.Gui - GitHub Copilot Instructions
+# Terminal.Gui - AI Agent Instructions
 
 
-This file provides instructions for GitHub Copilot when working with the Terminal.Gui project.
+> **📘 For complete contributor guidelines (humans and AI agents), see [CONTRIBUTING.md](CONTRIBUTING.md).**
 
 
-## Project Overview
+This repository uses [CONTRIBUTING.md](CONTRIBUTING.md) as the single source of truth for code style, testing, CI/CD, and contribution workflow. GitHub Copilot and other AI coding agents should also refer to [.github/copilot-instructions.md](.github/copilot-instructions.md) for a curated summary of non-negotiable rules.
 
 
-**Terminal.Gui** is a cross-platform UI toolkit for creating console-based graphical user interfaces in .NET. It provides a comprehensive framework for building interactive console applications with support for keyboard and mouse input, customizable views, and a robust event system. The toolkit works across Windows, macOS, and Linux, leveraging platform-specific console capabilities where available.
-
-**Key characteristics:**
-- Cross-platform terminal/console UI framework for .NET
-- Supports Windows, macOS, and Linux
-- Rich GUI controls (buttons, dialogs, menus, text boxes, etc.)
-- Keyboard-first design with full mouse support
-- Follows Microsoft .NET Framework Design Guidelines, with some tweaks.
-- v2 is currently in Alpha with stable core API (v1 is in maintenance mode)
-
-## Documentation
-
-- Full documentation: [gui-cs.github.io/Terminal.Gui](https://gui-cs.github.io/Terminal.Gui)
-- API Reference: [API Documentation](https://gui-cs.github.io/Terminal.Gui/api/Terminal.Gui.App.html)
-- Getting Started: [Getting Started Guide](https://gui-cs.github.io/Terminal.Gui/docs/getting-started)
-
-## Repository Structure
-
-- `/Terminal.Gui/` - Core library source code
-  - `App/` - Core application logic, `Application.cs` (static class managing `RunState` and `MainLoop`)
-  - `Configuration/` - `ConfigurationManager` for application settings
-  - `Drivers/` - Console driver implementations (`IConsoleDriver.cs`, `NetDriver`, `UnixDriver`, `WindowsDriver`)
-  - `Drawing/` - Rendering graphical elements in the console
-  - `Input/` - Keyboard and mouse input handling
-  - `View/` - Core `View` class hierarchy
-  - `Views/` - Specific sub-classes of `View` (Toplevel, Window, Dialog, etc.)
-- `/Examples/` - Sample applications and demos
-- `/Examples/UICatalog/` - Comprehensive demo app for manual testing
-- `/Tests/` - Unit and integration tests
-- `/docfx/` - Documentation source files (Deep Dive Articles and API docs)
-- `/Scripts/` - Build and utility scripts
-
-## Branching Model
-
-**Terminal.Gui uses GitFlow:**
-- `v2_develop` - Default branch for v2 development (active development)
-- `v2_release` - Stable release branches matching NuGet packages
-
-## Code Style and Standards
-
-### Code Style Tenets
-
-1. **Six-Year-Old Reading Level** - Prioritize readability over terseness. Use clear variable names and comments.
-2. **Consistency, Consistency, Consistency** - Follow established patterns ruthlessly.
-3. **Don't be Weird** - Follow Microsoft and .NET community conventions.
-4. **Set and Forget** - Use ReSharper/Rider for automated formatting.
-5. **Documentation is the Spec** - API documentation is the source of truth.
-
-### Coding Conventions
-
-- Based on [Microsoft C# Coding Conventions](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions)
-- Project settings defined and enforced via `./Terminal.sln.DotSettings` and `./.editorconfig`
-- Use `var` only for the most basic dotnet types - prefer explicit types for clarity
-- Use target-typed new
-
-## API Design Guidelines
-
-### Public API Tenets
-
-1. **Stand on the shoulders of giants** - Follow [Microsoft .NET Framework Design Guidelines](https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/)
-2. **Don't Break Existing Stuff** - Avoid breaking changes; find compatible ways to add features
-3. **Fail-fast** - Prefer early failure to expose bugs sooner
-4. **Standards Reduce Complexity** - Use standard .NET idioms, tweaked to match Terminal.Gui. 
-
-### API Documentation Requirements
-
-**All public APIs must have XML documentation:**
-- Clear, concise, and complete `<summary>` tags
-- Use `<see cref=""/>` liberally for cross-references
-- Add `<remarks>` for context and detailed explanations
-- Document complex topics in `docfx/articles/*.md` files
-- Use proper English and correct grammar
-- Provide sample code via `<example>` in cases where a sample is needed (not for very obvious things)
-
-### Events
-
-- Follow the [Events Deep Dive](https://gui-cs.github.io/Terminal.Gui/docs/events.html) documentation
-- Use the Cancellable Work Pattern for user-initiated actions
-- Use the CWPHelpers if possible
-- Name event handlers consistently (e.g., `On[EventName]`), following dotnet guidelines.
-
-## User Experience Tenets
-
-1. **Honor What's Come Before** - Follow established Mac/Windows GUI idioms (e.g., `Ctrl-C` for copy)
-2. **Consistency Matters** - Common UI patterns should be consistent (e.g., `Ctrl-Q` quits modals)
-3. **Honor the OS, but Work Everywhere** - Take advantage of platform capabilities while maintaining cross-platform support
-4. **Keyboard first, Mouse also** - Optimize for keyboard, but ensure everything also works with mouse
-
-## Testing
-
-### Unit Test Requirements
-
-- **Never decrease code coverage** - Aim for 70%+ coverage on new code
-- Write unit tests for all new functionality
-- Follow existing test patterns in `/Tests/`
-- Many existing unit tests are obtuse and not really unit tests. Anytime new tests are added or updated, strive to refactor the tests into more granular tests where each test covers the smallest area possible. 
-- Many existing unit tests in the `./Tests/UnitTests` project incorrectly require `Application.Init` and use `[AutoInitShutdown]`. Anytime new tests are added or updated, strive to remove these dependencies and make the tests parallelizable. This means not taking any dependency on static objects like `Application` and `ConfigurationManager`. 
-
-## Pull Request Guidelines
-
-- Titles should be of the form "Fixes #issue. Terse description." 
-- If the PR addresses multiple issues, use "Fixes #issue1, #issue2. Terse description."
-- First comment should include "- Fixes #issue" for each issue addressed. If an issue is only partially addressed, use "Partially addresses #issue".
-- First comment should include a thorough description of the change and any impact. 
-- Put temporary .md files in `/docfx/docs/drafts/` and remove before merging.
-
-## Building and Running
-
-### Build the Solution
-```powershell
-dotnet build
-```
-
-### Run Tests
-```powershell
-dotnet test
-```
-
-## Key Concepts
-
-`./docfx/docs` contains a set of architectural and key-concept deep-dives. 
-
-## Additional Guidelines
-1. Maintain existing code structure and organization unless explicitly told
-2. View sub-classes must not use private APIs
-3. Suggest changes to the `./docfx/docs/` folder when appropriate
-
-## Working with Pull Request Branches
-
-When creating PRs, include instructions at the end of each PR description for how to pull the branch down locally. Use the following template, adapted for the typical remote setup where `origin` points to the user's fork and `upstream` points to `gui-cs/Terminal.Gui`:
-
-```markdown
-## How to Pull This PR Branch Locally
-
-If you want to test or modify this PR locally, use one of these approaches based on your remote setup:
-
-### Method 1: Fetch from upstream (if branch exists there)
-```bash
-# Fetch the branch from upstream
-git fetch upstream <branch-name>
-
-# Switch to the branch
-git checkout <branch-name>
-
-# Make your changes, then commit them
-git add .
-git commit -m "Your commit message"
-
-# Push to your fork (origin)
-git push origin <branch-name>
-```
-
-### Method 2: Fetch by PR number
-```bash
-# Fetch the PR branch from upstream by PR number
-git fetch upstream pull/<PR_NUMBER>/head:<branch-name>
-
-# Switch to the branch
-git checkout <branch-name>
-
-# Make your changes, then commit them
-git add .
-git commit -m "Your commit message"
-
-# Push to your fork (origin)
-git push origin <branch-name>
-```
-
-The PR will automatically update when you push to the branch in your fork.
-```
-
-**Note:** Adjust the remote names if your setup differs (e.g., if `origin` points to `gui-cs/Terminal.Gui` and you have a separate remote for your fork).
+**Key highlights for AI agents:**
+- Always use explicit types (no `var` except for built-in simple types)
+- Always use target-typed `new()` syntax
+- Add new tests to `Tests/UnitTestsParallelizable/` when possible
+- Never decrease code coverage
+- Follow `.editorconfig` and `Terminal.sln.DotSettings` for formatting
 
 
+See [CONTRIBUTING.md](CONTRIBUTING.md) for complete details.

+ 346 - 124
CONTRIBUTING.md

@@ -1,191 +1,413 @@
 # Contributing to Terminal.Gui
 # Contributing to Terminal.Gui
 
 
-We welcome contributions from the community. See [Issues](https://github.com/gui-cs/Terminal.Gui/issues) for a list of open [bugs](https://github.com/gui-cs/Terminal.Gui/issues?q=is%3Aopen+is%3Aissue+label%3Abug) and [enhancements](https://github.com/gui-cs/Terminal.Gui/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement). Contributors looking for something fun to work on should look at issues tagged as:
+> **📘 This document is the single source of truth for all contributors (humans and AI agents) to Terminal.Gui.**
 
 
-- [good first issue](https://github.com/gui-cs/Terminal.Gui/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)
-- [up for grabs](https://github.com/gui-cs/Terminal.Gui/issues?q=is%3Aopen+is%3Aissue+label%3Aup-for-grabs)
-- [help wanted](https://github.com/gui-cs/Terminal.Gui/issues?q=is%3Aopen+is%3Aissue+label%3Aup-for-grabs)
+Welcome! This guide provides everything you need to know to contribute effectively to Terminal.Gui, including project structure, build instructions, coding conventions, testing requirements, and CI/CD workflows.
 
 
-## Forking and Submitting Changes
+## Table of Contents
 
 
-Terminal.Gui uses the [GitFlow](https://nvie.com/posts/a-successful-git-branching-model/) branching model. 
+- [Project Overview](#project-overview)
+- [Building and Testing](#building-and-testing)
+- [Coding Conventions](#coding-conventions)
+- [Testing Requirements](#testing-requirements)
+- [API Documentation Requirements](#api-documentation-requirements)
+- [Pull Request Guidelines](#pull-request-guidelines)
+- [CI/CD Workflows](#cicd-workflows)
+- [Repository Structure](#repository-structure)
+- [Branching Model](#branching-model)
+- [Key Architecture Concepts](#key-architecture-concepts)
+- [What NOT to Do](#what-not-to-do)
+- [Additional Resources](#additional-resources)
 
 
-* The `v1_release` and `v2_release` branches are always stable, and always match the most recently released Nuget package.
-* The `v1_develop` and `v2_develop` branches are where new development and bug-fixes happen. `v2_develop` is the default Github branch.
+---
 
 
-### Forking Terminal.Gui
+## Project Overview
 
 
-1. Use GitHub to fork the `Terminal.Gui` repo to your account (https://github.com/gui-cs/Terminal.Gui/fork).
+**Terminal.Gui** is a cross-platform UI toolkit for creating console-based graphical user interfaces in .NET. It's a large codebase (~1,050 C# files, 333MB) providing a comprehensive framework for building interactive console applications with support for keyboard and mouse input, customizable views, and a robust event system.
 
 
-2. Clone your fork to your local machine
+**Key characteristics:**
+- **Language**: C# (net8.0)
+- **Size**: ~496 source files in core library, ~1,050 total C# files
+- **Platform**: Cross-platform (Windows, macOS, Linux)
+- **Architecture**: Console UI toolkit with driver-based architecture
+- **Version**: v2 (Alpha), v1 (maintenance mode)
+- **Branching**: GitFlow model (v2_develop is default/active development)
 
 
-```
-git clone https://github.com/<yourID>/Terminal.Gui
-```
+---
 
 
-Now, your local repo will have an `origin` remote pointing to `https://github.com/<yourID>/Terminal.Gui`.
+## Building and Testing
 
 
-3. Add a remote for `upstream`: 
-```
-git remote add upstream https://github.com/gui-cs/Terminal.Gui
-```
-You now have your own fork and a local repo that references it as `origin`. Your local repo also now references the orignal Terminal.Gui repo as `upstream`. 
+### Required Tools
 
 
-### Starting to Make a Change
+- **.NET SDK**: 8.0.0 (see `global.json`)
+- **Runtime**: .NET 8.x (latest GA)
+- **Optional**: ReSharper/Rider for code formatting (honor `.editorconfig` and `Terminal.sln.DotSettings`)
 
 
-Ensure your local `v1_develop` (for v1) or `v2_develop` (for v2) branch is up-to-date with `upstream` (`github.com/gui-cs/Terminal.Gui`):
-```powershell
-cd ./Terminal.Gui
-git checkout v2_develop
-git pull upstream v2_develop
-```
+### Build Commands (In Order)
 
 
-Create a new local branch:
-```powershell
-git checkout -b my_new_branch
-```
+**ALWAYS run these commands from the repository root:**
 
 
-### Making Changes
-Follow all the guidelines below.
+1. **Restore packages** (required first, ~15-20 seconds):
+   ```bash
+   dotnet restore
+   ```
 
 
-* [Coding Style](#TerminalGui-Coding-Style)
-* [Unit Tests](#Unit-Tests)
-* [Sample Code](#Sample-Code)
-* API Documentation
-* etc...
+2. **Build solution** (Debug, ~50 seconds):
+   ```bash
+   dotnet build --configuration Debug --no-restore
+   ```
+   - Expect ~326 warnings (nullable reference warnings, unused variables, etc.) - these are normal
+   - 0 errors expected
 
 
-When you're ready, commit your changes:
+3. **Build Release** (for packaging):
+   ```bash
+   dotnet build --configuration Release --no-restore
+   ```
 
 
-```powershell
-git add .
-git commit -m "Fixes #1234. Some bug"
-```
+### Test Commands
+
+**Two test projects exist:**
+
+1. **Non-parallel tests** (depend on static state, ~10 min timeout):
+   ```bash
+   dotnet test Tests/UnitTests --no-build --verbosity normal
+   ```
+   - Uses `Application.Init` and static state
+   - Cannot run in parallel
+   - Includes `--blame` flags for crash diagnostics
 
 
-### Submitting a Pull Request
+2. **Parallel tests** (can run concurrently, ~10 min timeout):
+   ```bash
+   dotnet test Tests/UnitTestsParallelizable --no-build --verbosity normal
+   ```
+   - No dependencies on static state
+   - **Preferred for new tests**
 
 
-1. Push your local branch to your fork (`origin`):
+3. **Integration tests**:
+   ```bash
+   dotnet test Tests/IntegrationTests --no-build --verbosity normal
+   ```
 
 
-```powershell
-git push --set-upstream origin my_new_branch
+**Important**: Tests may take significant time. CI uses blame flags for crash detection:
+```bash
+--diag:logs/UnitTests/logs.txt --blame --blame-crash --blame-hang --blame-hang-timeout 60s --blame-crash-collect-always
 ```
 ```
 
 
-2. Create the Pull Request:
+### Common Build Issues
+
+#### Issue: Build Warnings
+- **Expected**: ~326 warnings (nullable refs, unused vars, xUnit suggestions)
+- **Action**: Don't add new warnings; fix warnings in code you modify
+
+#### Issue: Test Timeouts
+- **Expected**: Tests can take 5-10 minutes
+- **Action**: Use appropriate timeout values (60-120 seconds for test commands)
+
+#### Issue: Restore Failures
+- **Solution**: Ensure `dotnet restore` completes before building
+- **Note**: Takes 15-20 seconds on first run
 
 
-In the output of the `git push` command you'll see instructions with a link to the Pull Request:
+#### Issue: NativeAot/SelfContained Build
+- **Solution**: Restore these projects explicitly:
+  ```bash
+  dotnet restore ./Examples/NativeAot/NativeAot.csproj -f
+  dotnet restore ./Examples/SelfContained/SelfContained.csproj -f
+  ```
 
 
-```powershell
- $ git push --set-upstream origin my_new_branch
-Enumerating objects: 8, done.
-...
-remote:
-remote: Create a pull request for 'my_new_branch' on GitHub by visiting:
-remote:      https://github.com/<yourID>/Terminal.Gui/pull/new/more_doc_fixes
-remote:
-...
+### Running Examples
+
+**UICatalog** (comprehensive demo app):
+```bash
+dotnet run --project Examples/UICatalog/UICatalog.csproj
 ```
 ```
 
 
-3. Go to that URL and create the Pull Request:
+---
 
 
-(in Windows Terminal, just CTRL-Click on the URL)
+## Coding Conventions
 
 
-Follow the template instructions found on Github.
+### Code Style Tenets
 
 
-## Tenets for [gui-cs](www.github.com/gui-cs) Code Style (Unless you have better ones)
+1. **Six-Year-Old Reading Level** - Readability over terseness
+2. **Consistency, Consistency, Consistency** - Follow existing patterns ruthlessly
+3. **Don't be Weird** - Follow Microsoft/.NET conventions
+4. **Set and Forget** - Rely on automated tooling
+5. **Documentation is the Spec** - API docs are source of truth
 
 
-* **Six-Year-Old Reading Level** - Our code style is biased towards code readability and away from terseness. This is *Systems Software* and needs to stand the test of time. Code should be structured and use variable names that make it readable by a 6-year-old, and comments in code are encouraged. 
-* **Consistency, Consistency, Consistency** - We adopt and document our standards for code style and then enforce them ruthlessly. For example, we require code reviews to pay attention to code style, not just functionality. 
-* **Don't be Weird** - Like all developers we have opinions, but our opinions on code style are tempered by existing standards. We are biased towards code style that used by Microsoft and other leading dotnet developers. For example, we choose 4 spaces for indentation instead of 8.
-* **Set and Forget** - We embrace and encourage the use of technology that makes it easy for contributors to apply best-practice code-style, such as ReSharper. As we do so we are mindful that tools can cause hidden issues and merge hell.
-* **Documentation is the Spec** - We care deeply about providing delightful developer documentation and are sticklers for grammar and clarity. If the code and the docs conflict, we are biased to believe what we wrote in the API documentation. This drives a virtuous cycle of clear thinking.
+### Code Formatting
 
 
-**Terminal.Gui** uses a derivative of the [Microsoft C# Coding Conventions](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions), with any deviations from those (somewhat older) conventions codified in the .editorconfig for the solution, as well as even more specific definitions in team-shared dotsettings files, used by ReSharper and Rider.\
-Before you commit code, please run the formatting rules on **only the code file(s) you have modified**, in one of the following ways, in order of most preferred to least preferred:
+- **Do NOT add formatting tools** - Use existing `.editorconfig` and `Terminal.sln.DotSettings`
+- Format code with:
+  1. ReSharper/Rider (`Ctrl-E-C`)
+  2. JetBrains CleanupCode CLI tool (free)
+  3. Visual Studio (`Ctrl-K-D`) as fallback
+- **Only format files you modify**
 
 
- 1. `Ctrl-E-C` if using ReSharper or Rider
- 2. Running the free [CleanupCode](https://www.jetbrains.com/help/resharper/CleanupCode.html) tool from JetBrains (this applies the same formatting rules as if you had used ReSharper or Rider, but is free for all users, if you don't have a license for those products)
-     - Run at the command line, from the solution root directory, as: `cleanupcode.exe relative/path/to/your/file.cs`
- 3. If you are unable to use either of those options, the last resort is to use `Ctrl-K-D` in Visual Studio (with default C# developer key bindings), to apply the subset of the formatting rules that Visual Studio can apply.
+### Critical Coding Rules
 
 
-## User Experience Tenets
+**⚠️ CRITICAL - These rules MUST be followed in ALL new or modified code:**
 
 
-**Terminal.Gui**, as a UI framework, heavily influences how console graphical user interfaces (GUIs) work. We use the following [tenets](https://ceklog.kindel.com/2020/02/10/tenets/) to guide us:
+#### Type Declarations and Object Creation
 
 
-*NOTE: Like all tenets, these are up for debate. If you disagree, have questions, or suggestions about these tenets and guidelines submit an Issue using the [design](https://github.com/gui-cs/Terminal.Gui/issues?q=is%3Aopen+is%3Aissue+label%3Adesign) tag.*
+- **ALWAYS use explicit types** - Never use `var` except for built-in simple types (`int`, `string`, `bool`, `double`, `float`, `decimal`, `char`, `byte`)
+  ```csharp
+  // ✅ CORRECT - Explicit types
+  View view = new () { Width = 10 };
+  MouseEventArgs args = new () { Position = new Point(5, 5) };
+  List<View?> views = new ();
+  var count = 0;  // OK - int is a built-in type
+  var name = "test";  // OK - string is a built-in type
+  
+  // ❌ WRONG - Using var for non-built-in types
+  var view = new View { Width = 10 };
+  var args = new MouseEventArgs { Position = new Point(5, 5) };
+  var views = new List<View?>();
+  ```
 
 
-1. **Honor What's Come Before**. The Mac and Windows OS's have well-established GUI idioms that are mostly consistent. We adhere to these versus inventing new ways for users to do things. For example, **Terminal.Gui** adopts the `ctrl/command-c`, `ctrl/command-v`, and `ctrl/command-x` keyboard shortcuts for cut, copy, and paste versus defining new shortcuts.
-2. **Consistency Matters**. Common UI idioms should be consistent across the GUI framework. For example, `ctrl/command-q` quits/exits all modal views. See [Issue #456](https://github.com/gui-cs/Terminal.Gui/issues/456) as a counter-example that should be fixed.
-3. **Honor the OS, but Work Everywhere**. **Terminal.Gui** is cross-platform, but we support taking advantage of a platform's unique advantages. For example, the Windows Console API is richer than the Unix API in terms of keyboard handling. Thus, in Windows pressing the `alt` key in a **Terminal.Gui** app will activate the `MenuBar`, but in Unix, the user has to press the full hotkey (e.g. `alt-f`) or `F9`. 
-4. **Keyboard first, Mouse also**. Users use consoles primarily with the keyboard; **Terminal.Gui** is optimized for getting stuff done without using the Mouse. However, as a GUI framework, the Mouse is essential thus we strive to ensure that everything also works via the Mouse.
+- **ALWAYS use target-typed `new()`** - Use `new ()` instead of `new TypeName()` when the type is already declared
+  ```csharp
+  // ✅ CORRECT - Target-typed new
+  View view = new () { Width = 10 };
+  MouseEventArgs args = new ();
+  
+  // ❌ WRONG - Redundant type name
+  View view = new View() { Width = 10 };
+  MouseEventArgs args = new MouseEventArgs();
+  ```
 
 
-## Public API Tenets & Guidelines
+#### Other Conventions
 
 
-**Terminal.Gui** provides an API that is used by many. As the project evolves, contributors should follow these [tenets](https://ceklog.kindel.com/2020/02/10/tenets/) to ensure Consistency and backward compatibility.
+- Follow `.editorconfig` settings (e.g., braces on new lines, spaces after keywords)
+- 4-space indentation
+- No trailing whitespace
+- File-scoped namespaces
 
 
-*NOTE: Like all tenets, these are up for debate. If you disagree, have questions, or suggestions about these tenets and guidelines submit an Issue using the [design](https://github.com/gui-cs/Terminal.Gui/issues?q=is%3Aopen+is%3Aissue+label%3Adesign) tag.*
+**⚠️ CRITICAL - These conventions apply to ALL code - production code, test code, examples, and samples.**
 
 
-1. **Stand on the shoulders of giants.** Follow the [Microsoft .NET Framework Design Guidelines](https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/) where appropriate. 
-2. **Don't Break Existing Stuff.** Avoid breaking changes to user behavior or the public API; instead, figure out how to implement new functionality in a similar way. If a breaking change can't be avoided, follow the guidelines below.
-3. **Fail-fast.** Fail-fast makes bugs and failures appear sooner, leading to a higher-quality framework and API.
-4. **Standards Reduce Complexity**. We strive to adopt standard API idoms because doing so reduces complexity for users of the API. For example, see Tenet #1 above. A counterexample is [Issue #447](https://github.com/gui-cs/Terminal.Gui/issues/447).
+---
 
 
-### Include API Documentation
+## Testing Requirements
 
 
-Great care has been provided thus far in ensuring **Terminal.Gui** has great [API Documentation](https://gui-cs.github.io/Terminal.Gui). Contributors have the responsibility of continuously improving the API Documentation.
+### Code Coverage
 
 
-- All public APIs must have clear, concise, and complete documentation in the form of [XML Documentation](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/xmldoc/).
-- Keep the `<summary></summary>` terse.
-- Use `<see cref=""/>` liberally to cross-link topics.
-- Use `<remarks></remarks>` to add more context and explanation.
-- For complex topics, provide conceptual documentation in the `docfx/articles` folder as a `.md` file. It will automatically get picked up and be added to [Conceptual Documentation](https://gui-cs.github.io/Terminal.Gui/docs/index.html).
-- Use proper English and good grammar.
+- **Never decrease code coverage** - PRs must maintain or increase coverage
+- Target: 70%+ coverage for new code
+- CI monitors coverage on each PR
 
 
-### Defining Events
+### Test Patterns
 
 
-See https://gui-cs.github.io/Terminal.Gui/docs/events.html
+- **Parallelizable tests preferred** - Add new tests to `UnitTestsParallelizable` when possible
+- **Avoid static dependencies** - Don't use `Application.Init`, `ConfigurationManager` in tests
+- **Don't use `[AutoInitShutdown]`** - Legacy pattern, being phased out
+- **Make tests granular** - Each test should cover smallest area possible
+- Follow existing test patterns in respective test projects
 
 
+### Test Configuration
 
 
-### Defining new `View` classes
+- `xunit.runner.json` - xUnit configuration
+- `coverlet.runsettings` - Coverage settings (OpenCover format)
 
 
-- Support parameterless constructors (see [Issue 102](Parameterless constructors #102)). Do not require callers to use a parameterized constructor except when forcing `Absolute Layout`).
-- Avoid doing initialization via constructors. Instead use a property so consumers can use object initialization (e.g. `var foo = new Foo() { a = b };`).
-- Ensure the `UICatalog` demo for the new class illustrates both `Absolutle Layout` and `Computed Layout`.
+---
 
 
-## Breaking Changes to User Behavior or the Public API
+## API Documentation Requirements
 
 
-- Tag all pull requests that cause breaking changes to user behavior or the public API with the [breaking-change](https://github.com/gui-cs/Terminal.Gui/issues?q=is%3Aopen+is%3Aissue+label%3Abreaking-change) tag. This will help project maintainers track and document these.
-- Add a `<remark></remark>` to the XML Documentation to the code describing the breaking change. These will get picked up in the [API Documentation](https://gui-cs.github.io/Terminal.Gui/api/Terminal.Gui.html).
+**All public APIs MUST have XML documentation:**
 
 
-## Unit Tests
+- Clear, concise `<summary>` tags
+- Use `<see cref=""/>` for cross-references
+- Add `<remarks>` for context
+- Include `<example>` for non-obvious usage
+- Complex topics → `docfx/docs/*.md` files
+- Proper English and grammar - Clear, concise, complete. Use imperative mood.
 
 
-PRs should never cause code coverage to go down. Ideally, every PR will get the project closer to 100%. PRs that include new functionality (e.g. a new control) should have at least 70% code coverage for the new functionality. 
+---
 
 
-**Terminal.Gui** has an automated unit or regression test suite. See the [Testing wiki](https://github.com/gui-cs/Terminal.Gui/wiki/Testing).
+## Pull Request Guidelines
 
 
-We analyze unit tests and code coverage on each PR push. 
+### PR Requirements
 
 
-The code coverage of the latest released build (on NuGet) is shown as a badge at the top of `README.md`. Here as well:
+- **Title**: "Fixes #issue. Terse description". If multiple issues, list all, separated by commas (e.g. "Fixes #123, #456. Terse description")
+- **Description**: 
+  - Include "- Fixes #issue" for each issue near the top
+  - **ALWAYS** include instructions for pulling down locally at end of Description
+  - Suggest user setup a remote named `copilot` pointing to your fork
+  - Example:
+    ```markdown
+    # To pull down this PR locally:
+    git remote add copilot <your-fork-url>
+    git fetch copilot <branch-name>
+    git checkout copilot/<branch-name>
+    ```
+- **Coding Style**: Follow all coding conventions in this document for new and modified code
+- **Tests**: Add tests for new functionality (see [Testing Requirements](#testing-requirements))
+- **Coverage**: Maintain or increase code coverage
+- **Scenarios**: Update UICatalog scenarios when adding features
 
 
-![Code Coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/migueldeicaza/90ef67a684cb71db1817921a970f8d27/raw/code-coverage.json)
+---
 
 
-The project uses Fine Code Coverage to allow easy access to code coverage info on a per-component basis.
+## CI/CD Workflows
 
 
-Use the following command to generate the same CC info that the Publish Github Action uses to publish the results to the badge:
+The repository uses multiple GitHub Actions workflows. What runs and when:
+
+### 1) Build Solution (`.github/workflows/build.yml`)
+
+- **Triggers**: push and pull_request to `v2_release`, `v2_develop` (ignores `**.md`); supports `workflow_call`
+- **Runner/timeout**: `ubuntu-latest`, 10 minutes
+- **Steps**:
+  - Checkout and setup .NET 8.x GA
+  - `dotnet restore`
+  - Build Debug: `dotnet build --configuration Debug --no-restore -property:NoWarn=0618%3B0612`
+  - Build Release (library): `dotnet build Terminal.Gui/Terminal.Gui.csproj --configuration Release --no-incremental --force -property:NoWarn=0618%3B0612`
+  - Pack Release: `dotnet pack Terminal.Gui/Terminal.Gui.csproj --configuration Release --output ./local_packages -property:NoWarn=0618%3B0612`
+  - Restore NativeAot/SelfContained examples, then restore solution again
+  - Build Release for `Examples/NativeAot` and `Examples/SelfContained`
+  - Build Release solution
+  - Upload artifacts named `build-artifacts`, retention 1 day
 
 
-```
-dotnet test --no-restore --verbosity normal --collect:"XPlat Code Coverage"  --settings UnitTests/coverlet.runsettings
-```
+### 2) Build & Run Unit Tests (`.github/workflows/unit-tests.yml`)
+
+- **Triggers**: push and pull_request to `v2_release`, `v2_develop` (ignores `**.md`)
+- **Process**: Calls build workflow, then runs:
+  - Non-parallel UnitTests on Ubuntu/Windows/macOS matrix with coverage and blame/diag flags; `xunit.stopOnFail=false`
+  - Parallel UnitTestsParallelizable similarly with coverage; `xunit.stopOnFail=false`
+  - Uploads logs per-OS
+
+### 3) Build & Run Integration Tests (`.github/workflows/integration-tests.yml`)
+
+- **Triggers**: push and pull_request to `v2_release`, `v2_develop` (ignores `**.md`)
+- **Process**: Calls build workflow, then runs IntegrationTests on matrix with blame/diag; `xunit.stopOnFail=true`
+- Uploads logs per-OS
 
 
-Then open up the resulting `coverage.opencover.xml` file and you'll see the `sequenceCoverage` value:
+### 4) Publish to NuGet (`.github/workflows/publish.yml`)
 
 
-```xml
-<?xml version="1.0" encoding="utf-8"?>
-<CoverageSession>
-  <Summary numSequencePoints="15817" visitedSequencePoints="7249" numBranchPoints="9379" visitedBranchPoints="3640" sequenceCoverage="45.83" branchCoverage="38.81" maxCyclomaticComplexity="10276" minCyclomaticComplexity="10276" visitedClasses="105" numClasses="141" visitedMethods="965" numMethods="1751" />
- 
+- **Triggers**: push to `v2_release`, `v2_develop`, and tags `v*` (ignores `**.md`)
+- Uses GitVersion to compute SemVer, builds Release, packs with symbols, and pushes to NuGet.org using `NUGET_API_KEY`
+
+### 5) Build and publish API docs (`.github/workflows/api-docs.yml`)
+
+- **Triggers**: push to `v1_release` and `v2_develop`
+- Builds DocFX site on Windows and deploys to GitHub Pages when `ref_name` is `v2_release` or `v2_develop`
+
+### Replicating CI Locally
+
+```bash
+# Full CI sequence:
+dotnet restore
+dotnet build --configuration Debug --no-restore
+dotnet test Tests/UnitTests --no-build --verbosity normal
+dotnet test Tests/UnitTestsParallelizable --no-build --verbosity normal
+dotnet build --configuration Release --no-restore
 ```
 ```
 
 
-## Sample Code
+---
+
+## Repository Structure
+
+### Root Directory Files
+
+- `Terminal.sln` - Main solution file
+- `Terminal.sln.DotSettings` - ReSharper code style settings
+- `.editorconfig` - Code formatting rules (111KB, extensive)
+- `global.json` - .NET SDK version pinning
+- `Directory.Build.props` - Common MSBuild properties
+- `Directory.Packages.props` - Central package version management
+- `GitVersion.yml` - Version numbering configuration
+- `CONTRIBUTING.md` - This file - contribution guidelines (source of truth)
+- `AGENTS.md` - Pointer to this file for AI agents
+- `README.md` - Project documentation
+
+### Main Directories
+
+**`/Terminal.Gui/`** - Core library (496 C# files):
+- `App/` - Application lifecycle (`Application.cs` static class, `RunState`, `MainLoop`)
+- `Configuration/` - `ConfigurationManager` for settings
+- `Drivers/` - Console driver implementations (`IConsoleDriver`, `NetDriver`, `UnixDriver`, `WindowsDriver`)
+- `Drawing/` - Rendering system (attributes, colors, glyphs)
+- `Input/` - Keyboard and mouse input handling
+- `ViewBase/` - Core `View` class hierarchy and layout
+- `Views/` - Specific View subclasses (Window, Dialog, Button, ListView, etc.)
+- `Text/` - Text manipulation and formatting
+- `FileServices/` - File operations and services
+
+**`/Tests/`**:
+- `UnitTests/` - Non-parallel tests (use `Application.Init`, static state)
+- `UnitTestsParallelizable/` - Parallel tests (no static dependencies) - **Preferred**
+- `IntegrationTests/` - Integration tests
+- `StressTests/` - Long-running stress tests (scheduled daily)
+- `coverlet.runsettings` - Code coverage configuration
+
+**`/Examples/`**:
+- `UICatalog/` - Comprehensive demo app for manual testing
+- `Example/` - Basic example
+- `NativeAot/`, `SelfContained/` - Deployment examples
+- `ReactiveExample/`, `CommunityToolkitExample/` - Integration examples
+
+**`/docfx/`** - Documentation source:
+- `docs/` - Conceptual documentation (deep dives)
+- `api/` - Generated API docs (gitignored)
+- `docfx.json` - DocFX configuration
+
+**`/Scripts/`** - PowerShell build utilities (requires PowerShell 7.4+)
+
+**`/.github/workflows/`** - CI/CD pipelines (see [CI/CD Workflows](#cicd-workflows))
+
+---
+
+## Branching Model
+
+### GitFlow Model
+
+- `v2_develop` - Default branch, active development
+- `v2_release` - Stable releases, matches NuGet
+- `v1_develop`, `v1_release` - Legacy v1 (maintenance only)
+
+---
+
+## Key Architecture Concepts
+
+**⚠️ CRITICAL - Contributors should understand these concepts before starting work.**
+
+See `/docfx/docs/` for deep dives on:
+
+- **Application Lifecycle** - How `Application.Init`, `Application.Run`, and `Application.Shutdown` work
+- **View Hierarchy** - Understanding `View`, `Toplevel`, `Window`, and view containment
+- **Layout System** - Pos, Dim, and automatic layout
+- **Event System** - How keyboard, mouse, and application events flow
+- **Driver Architecture** - How console drivers abstract platform differences
+- **Drawing Model** - How rendering works with Attributes, Colors, and Glyphs
+
+Key documentation:
+- [View Documentation](https://gui-cs.github.io/Terminal.Gui/docs/View.html)
+- [Events Deep Dive](https://gui-cs.github.io/Terminal.Gui/docs/events.html)
+- [Layout System](https://gui-cs.github.io/Terminal.Gui/docs/layout.html)
+- [Keyboard Handling](https://gui-cs.github.io/Terminal.Gui/docs/keyboard.html)
+- [Mouse Support](https://gui-cs.github.io/Terminal.Gui/docs/mouse.html)
+- [Drivers](https://gui-cs.github.io/Terminal.Gui/docs/drivers.html)
+
+---
+
+## What NOT to Do
+
+- ❌ Don't add new linters/formatters (use existing)
+- ❌ Don't modify unrelated code
+- ❌ Don't remove/edit unrelated tests
+- ❌ Don't break existing functionality
+- ❌ Don't add tests to `UnitTests` if they can be parallelizable
+- ❌ Don't use `Application.Init` in new tests
+- ❌ Don't decrease code coverage
+- ❌ **Don't use `var` for anything but built-in simple types** (use explicit types)
+- ❌ **Don't use redundant type names with `new`** (**ALWAYS PREFER** target-typed `new ()`)
+
+---
+
+## Additional Resources
 
 
-[UI Catalog](https://github.com/gui-cs/Terminal.Gui/tree/master/UICatalog) is a great sample app for manual testing.
+- **Full Documentation**: https://gui-cs.github.io/Terminal.Gui
+- **API Reference**: https://gui-cs.github.io/Terminal.Gui/api/Terminal.Gui.App.html
+- **Deep Dives**: `/docfx/docs/` directory
+- **Getting Started**: https://gui-cs.github.io/Terminal.Gui/docs/getting-started.html
+- **Migrating from v1 to v2**: https://gui-cs.github.io/Terminal.Gui/docs/migratingfromv1.html
+- **Showcase**: https://gui-cs.github.io/Terminal.Gui/docs/showcase.html
+
+---
 
 
-When adding new functionality, fixing bugs, or changing things, please either add a new `Scenario` to **UICatalog** or update an existing `Scenario` to fully illustrate your work and provide a test-case.
+**Thank you for contributing to Terminal.Gui!** 🎉

+ 1 - 1
README.md

@@ -86,7 +86,7 @@ Or, you can use the [Terminal.Gui.Templates](https://github.com/gui-cs/Terminal.
 
 
 # Contributing
 # Contributing
 
 
-See [CONTRIBUTING.md](./CONTRIBUTING.md).
+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).
 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).
 
 

+ 16 - 6
Terminal.Gui/App/Application.Driver.cs

@@ -7,18 +7,24 @@ public static partial class Application // Driver abstractions
     internal static bool _forceFakeConsole;
     internal static bool _forceFakeConsole;
 
 
     /// <summary>Gets the <see cref="IConsoleDriver"/> that has been selected. See also <see cref="ForceDriver"/>.</summary>
     /// <summary>Gets the <see cref="IConsoleDriver"/> that has been selected. See also <see cref="ForceDriver"/>.</summary>
-    public static IConsoleDriver? Driver { get; internal set; }
+    public static IConsoleDriver? Driver
+    {
+        get => ApplicationImpl.Instance.Driver;
+        internal set => ApplicationImpl.Instance.Driver = value;
+    }
 
 
-    // BUGBUG: Force16Colors should be nullable.
     /// <summary>
     /// <summary>
     ///     Gets or sets whether <see cref="Application.Driver"/> will be forced to output only the 16 colors defined in
     ///     Gets or sets whether <see cref="Application.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
     ///     <see cref="ColorName16"/>. The default is <see langword="false"/>, meaning 24-bit (TrueColor) colors will be output
     ///     as long as the selected <see cref="IConsoleDriver"/> supports TrueColor.
     ///     as long as the selected <see cref="IConsoleDriver"/> supports TrueColor.
     /// </summary>
     /// </summary>
     [ConfigurationProperty (Scope = typeof (SettingsScope))]
     [ConfigurationProperty (Scope = typeof (SettingsScope))]
-    public static bool Force16Colors { get; set; }
+    public static bool Force16Colors
+    {
+        get => ApplicationImpl.Instance.Force16Colors;
+        set => ApplicationImpl.Instance.Force16Colors = value;
+    }
 
 
-    // BUGBUG: ForceDriver should be nullable.
     /// <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.
@@ -28,11 +34,15 @@ public static partial class Application // Driver abstractions
     ///     with either `driver` or `driverName` specified.
     ///     with either `driver` or `driverName` specified.
     /// </remarks>
     /// </remarks>
     [ConfigurationProperty (Scope = typeof (SettingsScope))]
     [ConfigurationProperty (Scope = typeof (SettingsScope))]
-    public static string ForceDriver { get; set; } = string.Empty;
+    public static string ForceDriver
+    {
+        get => ApplicationImpl.Instance.ForceDriver;
+        set => ApplicationImpl.Instance.ForceDriver = value;
+    }
 
 
     /// <summary>
     /// <summary>
     /// Collection of sixel images to write out to screen when updating.
     /// Collection of sixel images to write out to screen when updating.
     /// Only add to this collection if you are sure terminal supports sixel format.
     /// Only add to this collection if you are sure terminal supports sixel format.
     /// </summary>
     /// </summary>
-    public static List<SixelToRender> Sixel = new List<SixelToRender> ();
+    public static List<SixelToRender> Sixel => ApplicationImpl.Instance.Sixel;
 }
 }

+ 1 - 1
Terminal.Gui/App/Application.Keyboard.cs

@@ -83,7 +83,7 @@ public static partial class Application // Keyboard handling
 
 
     internal static void AddKeyBindings ()
     internal static void AddKeyBindings ()
     {
     {
-        if (Keyboard is Keyboard keyboard)
+        if (Keyboard is KeyboardImpl keyboard)
         {
         {
             keyboard.AddKeyBindings ();
             keyboard.AddKeyBindings ();
         }
         }

+ 16 - 29
Terminal.Gui/App/Application.Initialization.cs → Terminal.Gui/App/Application.Lifecycle.cs

@@ -5,7 +5,7 @@ using System.Reflection;
 
 
 namespace Terminal.Gui.App;
 namespace Terminal.Gui.App;
 
 
-public static partial class Application // Initialization (Init/Shutdown)
+public static partial class Application // Lifecycle (Init/Shutdown)
 {
 {
 
 
     /// <summary>Initializes a new instance of a Terminal.Gui Application. <see cref="Shutdown"/> must be called when the application is closing.</summary>
     /// <summary>Initializes a new instance of a Terminal.Gui Application. <see cref="Shutdown"/> must be called when the application is closing.</summary>
@@ -24,7 +24,7 @@ public static partial class Application // Initialization (Init/Shutdown)
     ///     The <see cref="Run{T}"/> function combines
     ///     The <see cref="Run{T}"/> function combines
     ///     <see cref="Init(IConsoleDriver,string)"/> and <see cref="Run(Toplevel, Func{Exception, bool})"/>
     ///     <see cref="Init(IConsoleDriver,string)"/> and <see cref="Run(Toplevel, Func{Exception, bool})"/>
     ///     into a single
     ///     into a single
-    ///     call. An application cam use <see cref="Run{T}"/> without explicitly calling
+    ///     call. An application can use <see cref="Run{T}"/> without explicitly calling
     ///     <see cref="Init(IConsoleDriver,string)"/>.
     ///     <see cref="Init(IConsoleDriver,string)"/>.
     /// </para>
     /// </para>
     /// <param name="driver">
     /// <param name="driver">
@@ -63,7 +63,11 @@ public static partial class Application // Initialization (Init/Shutdown)
         ApplicationImpl.Instance.Init (driver, driverName ?? ForceDriver);
         ApplicationImpl.Instance.Init (driver, driverName ?? ForceDriver);
     }
     }
 
 
-    internal static int MainThreadId { get; set; } = -1;
+    internal static int MainThreadId
+    {
+        get => ((ApplicationImpl)ApplicationImpl.Instance).MainThreadId;
+        set => ((ApplicationImpl)ApplicationImpl.Instance).MainThreadId = value;
+    }
 
 
     // INTERNAL function for initializing an app with a Toplevel factory object, driver, and mainloop.
     // INTERNAL function for initializing an app with a Toplevel factory object, driver, and mainloop.
     //
     //
@@ -114,28 +118,9 @@ public static partial class Application // Initialization (Init/Shutdown)
         // or go through the modern application architecture
         // or go through the modern application architecture
         if (Driver is null)
         if (Driver is null)
         {
         {
-            //// Try to find a legacy IConsoleDriver type that matches the driver name
-            //bool useLegacyDriver = false;
-            //if (!string.IsNullOrEmpty (ForceDriver))
-            //{
-            //    (List<Type?> drivers, List<string?> driverTypeNames) = GetDriverTypes ();
-            //    Type? driverType = drivers.FirstOrDefault (t => t!.Name.Equals (ForceDriver, StringComparison.InvariantCultureIgnoreCase));
-                
-            //    if (driverType is { } && !typeof (IConsoleDriverFacade).IsAssignableFrom (driverType))
-            //    {
-            //        // This is a legacy driver (not a ConsoleDriverFacade)
-            //        Driver = (IConsoleDriver)Activator.CreateInstance (driverType)!;
-            //        useLegacyDriver = true;
-            //    }
-            //}
-            
-            //// Use the modern application architecture
-            //if (!useLegacyDriver)
-            {
-                ApplicationImpl.Instance.Init (driver, driverName);
-                Debug.Assert (Driver is { });
-                return;
-            }
+            ApplicationImpl.Instance.Init (driver, driverName);
+            Debug.Assert (Driver is { });
+            return;
         }
         }
 
 
         Debug.Assert (Navigation is null);
         Debug.Assert (Navigation is null);
@@ -199,7 +184,7 @@ public static partial class Application // Initialization (Init/Shutdown)
     private static void Driver_KeyUp (object? sender, Key e) { RaiseKeyUpEvent (e); }
     private static void Driver_KeyUp (object? sender, Key e) { RaiseKeyUpEvent (e); }
     private static void Driver_MouseEvent (object? sender, MouseEventArgs e) { RaiseMouseEvent (e); }
     private static void Driver_MouseEvent (object? sender, MouseEventArgs e) { RaiseMouseEvent (e); }
 
 
-    /// <summary>Gets of list of <see cref="IConsoleDriver"/> types and type names that are available.</summary>
+    /// <summary>Gets a list of <see cref="IConsoleDriver"/> types and type names that are available.</summary>
     /// <returns></returns>
     /// <returns></returns>
     [RequiresUnreferencedCode ("AOT")]
     [RequiresUnreferencedCode ("AOT")]
     public static (List<Type?>, List<string?>) GetDriverTypes ()
     public static (List<Type?>, List<string?>) GetDriverTypes ()
@@ -225,8 +210,6 @@ public static partial class Application // Initialization (Init/Shutdown)
                                         .Union (["dotnet", "windows", "unix", "fake"])
                                         .Union (["dotnet", "windows", "unix", "fake"])
                                         .ToList ()!;
                                         .ToList ()!;
 
 
-
-
         return (driverTypes, driverTypeNames);
         return (driverTypes, driverTypeNames);
     }
     }
 
 
@@ -247,7 +230,11 @@ public static partial class Application // Initialization (Init/Shutdown)
     ///     The <see cref="InitializedChanged"/> event is raised after the <see cref="Init"/> and <see cref="Shutdown"/> methods have been called.
     ///     The <see cref="InitializedChanged"/> event is raised after the <see cref="Init"/> and <see cref="Shutdown"/> methods have been called.
     /// </para>
     /// </para>
     /// </remarks>
     /// </remarks>
-    public static bool Initialized { get; internal set; }
+    public static bool Initialized
+    {
+        get => ApplicationImpl.Instance.Initialized;
+        internal set => ApplicationImpl.Instance.Initialized = value;
+    }
 
 
     /// <summary>
     /// <summary>
     ///     This event is raised after the <see cref="Init"/> and <see cref="Shutdown"/> methods have been called.
     ///     This event is raised after the <see cref="Init"/> and <see cref="Shutdown"/> methods have been called.

+ 50 - 252
Terminal.Gui/App/Application.Mouse.cs

@@ -5,176 +5,39 @@ namespace Terminal.Gui.App;
 
 
 public static partial class Application // Mouse handling
 public static partial class Application // Mouse handling
 {
 {
-    /// <summary>
-    /// INTERNAL API: Holds the last mouse position.
-    /// </summary>
-    internal static Point? LastMousePosition { get; set; }
-
     /// <summary>
     /// <summary>
     ///     Gets the most recent position of the mouse.
     ///     Gets the most recent position of the mouse.
     /// </summary>
     /// </summary>
-    public static Point? GetLastMousePosition () { return LastMousePosition; }
+    public static Point? GetLastMousePosition () { return Mouse.GetLastMousePosition (); }
 
 
     /// <summary>Disable or enable the mouse. The mouse is enabled by default.</summary>
     /// <summary>Disable or enable the mouse. The mouse is enabled by default.</summary>
     [ConfigurationProperty (Scope = typeof (SettingsScope))]
     [ConfigurationProperty (Scope = typeof (SettingsScope))]
-    public static bool IsMouseDisabled { get; set; }
-
-    /// <summary>
-    /// Static reference to the current <see cref="IApplication"/> <see cref="IMouseGrabHandler"/>.
-    /// </summary>
-    public static IMouseGrabHandler MouseGrabHandler
+    public static bool IsMouseDisabled
     {
     {
-        get => ApplicationImpl.Instance.MouseGrabHandler;
-        set => ApplicationImpl.Instance.MouseGrabHandler = value ??
-                                                           throw new ArgumentNullException(nameof(value));
+        get => Mouse.IsMouseDisabled;
+        set => Mouse.IsMouseDisabled = value;
     }
     }
 
 
     /// <summary>
     /// <summary>
-    ///     INTERNAL API: Called when a mouse event is raised by the driver. Determines the view under the mouse and
-    ///     calls the appropriate View mouse event handlers.
+    ///     Gets the <see cref="IMouse"/> instance that manages mouse event handling and state.
     /// </summary>
     /// </summary>
-    /// <remarks>This method can be used to simulate a mouse event, e.g. in unit tests.</remarks>
-    /// <param name="mouseEvent">The mouse event with coordinates relative to the screen.</param>
-    internal static void RaiseMouseEvent (MouseEventArgs mouseEvent)
-    {
-        if (Initialized)
-        {
-            // LastMousePosition is a static; only set if the application is initialized.
-            LastMousePosition = mouseEvent.ScreenPosition;
-        }
-
-        if (IsMouseDisabled)
-        {
-            return;
-        }
-
-        // The position of the mouse is the same as the screen position at the application level.
-        //Debug.Assert (mouseEvent.Position == mouseEvent.ScreenPosition);
-        mouseEvent.Position = mouseEvent.ScreenPosition;
-
-        List<View?> currentViewsUnderMouse = View.GetViewsUnderLocation (mouseEvent.ScreenPosition, ViewportSettingsFlags.TransparentMouse);
-
-        View? deepestViewUnderMouse = currentViewsUnderMouse.LastOrDefault ();
-
-        if (deepestViewUnderMouse is { })
-        {
-#if DEBUG_IDISPOSABLE
-            if (View.EnableDebugIDisposableAsserts && deepestViewUnderMouse.WasDisposed)
-            {
-                throw new ObjectDisposedException (deepestViewUnderMouse.GetType ().FullName);
-            }
-#endif
-            mouseEvent.View = deepestViewUnderMouse;
-        }
-
-        MouseEvent?.Invoke (null, mouseEvent);
-
-        if (mouseEvent.Handled)
-        {
-            return;
-        }
-
-        // Dismiss the Popover if the user presses mouse outside of it
-        if (mouseEvent.IsPressed
-            && Popover?.GetActivePopover () as View is { Visible: true } visiblePopover
-            && View.IsInHierarchy (visiblePopover, deepestViewUnderMouse, includeAdornments: true) is false)
-        {
-            ApplicationPopover.HideWithQuitCommand (visiblePopover);
-
-            // Recurse once so the event can be handled below the popover
-            RaiseMouseEvent (mouseEvent);
-
-            return;
-        }
-
-        if (HandleMouseGrab (deepestViewUnderMouse, mouseEvent))
-        {
-            return;
-        }
-
-        // May be null before the prior condition or the condition may set it as null.
-        // So, the checking must be outside the prior condition.
-        if (deepestViewUnderMouse is null)
-        {
-            return;
-        }
-
-        // if the mouse is outside the Application.Top or Application.Popover hierarchy, we don't want to
-        // send the mouse event to the deepest view under the mouse.
-        if (!View.IsInHierarchy (Application.Top, deepestViewUnderMouse, true) && !View.IsInHierarchy (Popover?.GetActivePopover () as View, deepestViewUnderMouse, true))
-        {
-            return;
-        }
-
-        // Create a view-relative mouse event to send to the view that is under the mouse.
-        MouseEventArgs viewMouseEvent;
-
-        if (deepestViewUnderMouse is Adornment adornment)
-        {
-            Point frameLoc = adornment.ScreenToFrame (mouseEvent.ScreenPosition);
-
-            viewMouseEvent = new ()
-            {
-                Position = frameLoc,
-                Flags = mouseEvent.Flags,
-                ScreenPosition = mouseEvent.ScreenPosition,
-                View = deepestViewUnderMouse
-            };
-        }
-        else if (deepestViewUnderMouse.ViewportToScreen (Rectangle.Empty with { Size = deepestViewUnderMouse.Viewport.Size }).Contains (mouseEvent.ScreenPosition))
-        {
-            Point viewportLocation = deepestViewUnderMouse.ScreenToViewport (mouseEvent.ScreenPosition);
-
-            viewMouseEvent = new ()
-            {
-                Position = viewportLocation,
-                Flags = mouseEvent.Flags,
-                ScreenPosition = mouseEvent.ScreenPosition,
-                View = deepestViewUnderMouse
-            };
-        }
-        else
-        {
-            // The mouse was outside any View's Viewport.
-            // Debug.Fail ("This should never happen. If it does please file an Issue!!");
-
-            return;
-        }
-
-        RaiseMouseEnterLeaveEvents (viewMouseEvent.ScreenPosition, currentViewsUnderMouse);
-
-        while (deepestViewUnderMouse.NewMouseEvent (viewMouseEvent) is not true && MouseGrabHandler.MouseGrabView is not { })
-        {
-            if (deepestViewUnderMouse is Adornment adornmentView)
-            {
-                deepestViewUnderMouse = adornmentView.Parent?.SuperView;
-            }
-            else
-            {
-                deepestViewUnderMouse = deepestViewUnderMouse.SuperView;
-            }
-
-            if (deepestViewUnderMouse is null)
-            {
-                break;
-            }
-
-            Point boundsPoint = deepestViewUnderMouse.ScreenToViewport (mouseEvent.ScreenPosition);
-
-            viewMouseEvent = new ()
-            {
-                Position = boundsPoint,
-                Flags = mouseEvent.Flags,
-                ScreenPosition = mouseEvent.ScreenPosition,
-                View = deepestViewUnderMouse
-            };
-        }
-    }
-
+    /// <remarks>
+    ///     <para>
+    ///         This property provides access to mouse-related functionality in a way that supports
+    ///         parallel test execution by avoiding static state.
+    ///     </para>
+    ///     <para>
+    ///         New code should use <c>Application.Mouse</c> instead of the static properties and methods
+    ///         for better testability. Legacy static properties like <see cref="IsMouseDisabled"/> and
+    ///         <see cref="GetLastMousePosition"/> are retained for backward compatibility.
+    ///     </para>
+    /// </remarks>
+    public static IMouse Mouse => ApplicationImpl.Instance.Mouse;
 
 
 #pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved
 #pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved
     /// <summary>
     /// <summary>
-    /// Raised when a mouse event occurs. Can be cancelled by setting <see cref="HandledEventArgs.Handled"/> to <see langword="true"/>.
+    ///     Raised when a mouse event occurs. Can be cancelled by setting <see cref="HandledEventArgs.Handled"/> to
+    ///     <see langword="true"/>.
     /// </summary>
     /// </summary>
     /// <remarks>
     /// <remarks>
     ///     <para>
     ///     <para>
@@ -184,59 +47,34 @@ public static partial class Application // Mouse handling
     ///         <see cref="MouseEventArgs.View"/> will be the deepest view under the mouse.
     ///         <see cref="MouseEventArgs.View"/> will be the deepest view under the mouse.
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
-    ///         <see cref="MouseEventArgs.Position"/> coordinates are view-relative. Only valid if <see cref="MouseEventArgs.View"/> is set.
+    ///         <see cref="MouseEventArgs.Position"/> coordinates are view-relative. Only valid if
+    ///         <see cref="MouseEventArgs.View"/> is set.
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
     ///         Use this even to handle mouse events at the application level, before View-specific handling.
     ///         Use this even to handle mouse events at the application level, before View-specific handling.
     ///     </para>
     ///     </para>
     /// </remarks>
     /// </remarks>
-    public static event EventHandler<MouseEventArgs>? MouseEvent;
-#pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved
-
-    internal static bool HandleMouseGrab (View? deepestViewUnderMouse, MouseEventArgs mouseEvent)
+    public static event EventHandler<MouseEventArgs>? MouseEvent
     {
     {
-        if (MouseGrabHandler.MouseGrabView is { })
-        {
-#if DEBUG_IDISPOSABLE
-            if (View.EnableDebugIDisposableAsserts && MouseGrabHandler.MouseGrabView.WasDisposed)
-            {
-                throw new ObjectDisposedException (MouseGrabHandler.MouseGrabView.GetType ().FullName);
-            }
-#endif
-
-            // If the mouse is grabbed, send the event to the view that grabbed it.
-            // The coordinates are relative to the Bounds of the view that grabbed the mouse.
-            Point frameLoc = MouseGrabHandler.MouseGrabView.ScreenToViewport (mouseEvent.ScreenPosition);
-
-            var viewRelativeMouseEvent = new MouseEventArgs
-            {
-                Position = frameLoc,
-                Flags = mouseEvent.Flags,
-                ScreenPosition = mouseEvent.ScreenPosition,
-                View = deepestViewUnderMouse ?? MouseGrabHandler.MouseGrabView
-            };
-
-            //System.Diagnostics.Debug.WriteLine ($"{nme.Flags};{nme.X};{nme.Y};{mouseGrabView}");
-            if (MouseGrabHandler.MouseGrabView?.NewMouseEvent (viewRelativeMouseEvent) is true)
-            {
-                return true;
-            }
-
-            // ReSharper disable once ConditionIsAlwaysTrueOrFalse
-            if (MouseGrabHandler.MouseGrabView is null && deepestViewUnderMouse is Adornment)
-            {
-                // The view that grabbed the mouse has been disposed
-                return true;
-            }
-        }
-
-        return false;
+        add => Mouse.MouseEvent += value;
+        remove => Mouse.MouseEvent -= value;
     }
     }
+#pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved
 
 
     /// <summary>
     /// <summary>
-    ///     INTERNAL: Holds the non-<see cref="ViewportSettingsFlags.TransparentMouse"/> views that are currently under the mouse.
+    ///     INTERNAL: Holds the non-<see cref="ViewportSettingsFlags.TransparentMouse"/> views that are currently under the
+    ///     mouse.
     /// </summary>
     /// </summary>
-    internal static List<View?> CachedViewsUnderMouse { get; } = [];
+    internal static List<View?> CachedViewsUnderMouse => Mouse.CachedViewsUnderMouse;
+
+    /// <summary>
+    ///     INTERNAL API: Holds the last mouse position.
+    /// </summary>
+    internal static Point? LastMousePosition
+    {
+        get => Mouse.LastMousePosition;
+        set => Mouse.LastMousePosition = value;
+    }
 
 
     /// <summary>
     /// <summary>
     ///     INTERNAL: Raises the MouseEnter and MouseLeave events for the views that are under the mouse.
     ///     INTERNAL: Raises the MouseEnter and MouseLeave events for the views that are under the mouse.
@@ -245,59 +83,19 @@ public static partial class Application // Mouse handling
     /// <param name="currentViewsUnderMouse">The most recent result from GetViewsUnderLocation().</param>
     /// <param name="currentViewsUnderMouse">The most recent result from GetViewsUnderLocation().</param>
     internal static void RaiseMouseEnterLeaveEvents (Point screenPosition, List<View?> currentViewsUnderMouse)
     internal static void RaiseMouseEnterLeaveEvents (Point screenPosition, List<View?> currentViewsUnderMouse)
     {
     {
-        // Tell any views that are no longer under the mouse that the mouse has left
-        List<View?> viewsToLeave = CachedViewsUnderMouse.Where (v => v is { } && !currentViewsUnderMouse.Contains (v)).ToList ();
-
-        foreach (View? view in viewsToLeave)
-        {
-            if (view is null)
-            {
-                continue;
-            }
-
-            view.NewMouseLeaveEvent ();
-            CachedViewsUnderMouse.Remove (view);
-        }
-
-        // Tell any views that are now under the mouse that the mouse has entered and add them to the list
-        foreach (View? view in currentViewsUnderMouse)
-        {
-            if (view is null)
-            {
-                continue;
-            }
-
-            if (CachedViewsUnderMouse.Contains (view))
-            {
-                continue;
-            }
-
-            CachedViewsUnderMouse.Add (view);
-            var raise = false;
-
-            if (view is Adornment { Parent: { } } adornmentView)
-            {
-                Point superViewLoc = adornmentView.Parent.SuperView?.ScreenToViewport (screenPosition) ?? screenPosition;
-                raise = adornmentView.Contains (superViewLoc);
-            }
-            else
-            {
-                Point superViewLoc = view.SuperView?.ScreenToViewport (screenPosition) ?? screenPosition;
-                raise = view.Contains (superViewLoc);
-            }
-
-            if (!raise)
-            {
-                continue;
-            }
+        Mouse.RaiseMouseEnterLeaveEvents (screenPosition, currentViewsUnderMouse);
+    }
 
 
-            CancelEventArgs eventArgs = new ();
-            bool? cancelled = view.NewMouseEnterEvent (eventArgs);
+    /// <summary>
+    ///     INTERNAL API: Called when a mouse event is raised by the driver. Determines the view under the mouse and
+    ///     calls the appropriate View mouse event handlers.
+    /// </summary>
+    /// <remarks>This method can be used to simulate a mouse event, e.g. in unit tests.</remarks>
+    /// <param name="mouseEvent">The mouse event with coordinates relative to the screen.</param>
+    internal static void RaiseMouseEvent (MouseEventArgs mouseEvent) { Mouse.RaiseMouseEvent (mouseEvent); }
 
 
-            if (cancelled is true || eventArgs.Cancel)
-            {
-                break;
-            }
-        }
-    }
+    /// <summary>
+    ///     INTERNAL: Clears mouse state during application reset.
+    /// </summary>
+    internal static void ResetMouseState () { Mouse.ResetState (); }
 }
 }

+ 5 - 1
Terminal.Gui/App/Application.Navigation.cs

@@ -7,7 +7,11 @@ public static partial class Application // Navigation stuff
     /// <summary>
     /// <summary>
     ///     Gets the <see cref="ApplicationNavigation"/> instance for the current <see cref="Application"/>.
     ///     Gets the <see cref="ApplicationNavigation"/> instance for the current <see cref="Application"/>.
     /// </summary>
     /// </summary>
-    public static ApplicationNavigation? Navigation { get; internal set; }
+    public static ApplicationNavigation? Navigation
+    {
+        get => ApplicationImpl.Instance.Navigation;
+        internal set => ApplicationImpl.Instance.Navigation = value;
+    }
 
 
     /// <summary>Alternative key to navigate forwards through views. Ctrl+Tab is the primary key.</summary>
     /// <summary>Alternative key to navigate forwards through views. Ctrl+Tab is the primary key.</summary>
     [ConfigurationProperty (Scope = typeof (SettingsScope))]
     [ConfigurationProperty (Scope = typeof (SettingsScope))]

+ 5 - 1
Terminal.Gui/App/Application.Popover.cs

@@ -5,5 +5,9 @@ namespace Terminal.Gui.App;
 public static partial class Application // Popover handling
 public static partial class Application // Popover handling
 {
 {
     /// <summary>Gets the Application <see cref="Popover"/> manager.</summary>
     /// <summary>Gets the Application <see cref="Popover"/> manager.</summary>
-    public static ApplicationPopover? Popover { get; internal set; }
+    public static ApplicationPopover? Popover
+    {
+        get => ApplicationImpl.Instance.Popover;
+        internal set => ApplicationImpl.Instance.Popover = value;
+    }
 }
 }

+ 11 - 88
Terminal.Gui/App/Application.Run.cs

@@ -4,7 +4,7 @@ using System.Diagnostics.CodeAnalysis;
 
 
 namespace Terminal.Gui.App;
 namespace Terminal.Gui.App;
 
 
-public static partial class Application // Run (Begin, Run, End, Stop)
+public static partial class Application // Run (Begin -> Run -> Layout/Draw -> End -> Stop)
 {
 {
     /// <summary>Gets or sets the key to quit the application.</summary>
     /// <summary>Gets or sets the key to quit the application.</summary>
     [ConfigurationProperty (Scope = typeof (SettingsScope))]
     [ConfigurationProperty (Scope = typeof (SettingsScope))]
@@ -61,19 +61,10 @@ public static partial class Application // Run (Begin, Run, End, Stop)
     {
     {
         ArgumentNullException.ThrowIfNull (toplevel);
         ArgumentNullException.ThrowIfNull (toplevel);
 
 
-        //#if DEBUG_IDISPOSABLE
-        //        Debug.Assert (!toplevel.WasDisposed);
-
-        //        if (_cachedRunStateToplevel is { } && _cachedRunStateToplevel != toplevel)
-        //        {
-        //            Debug.Assert (_cachedRunStateToplevel.WasDisposed);
-        //        }
-        //#endif
-
         // Ensure the mouse is ungrabbed.
         // Ensure the mouse is ungrabbed.
-        if (MouseGrabHandler.MouseGrabView is { })
+        if (Mouse.MouseGrabView is { })
         {
         {
-            MouseGrabHandler.UngrabMouse ();
+            Mouse.UngrabMouse ();
         }
         }
 
 
         var rs = new RunState (toplevel);
         var rs = new RunState (toplevel);
@@ -130,11 +121,6 @@ public static partial class Application // Run (Begin, Run, End, Stop)
                     TopLevels.Push (toplevel);
                     TopLevels.Push (toplevel);
                 }
                 }
             }
             }
-
-            //if (TopLevels.FindDuplicates (new ToplevelEqualityComparer ()).Count > 0)
-            //{
-            //    throw new ArgumentException ("There are duplicates Toplevel IDs");
-            //}
         }
         }
 
 
         if (Top is null)
         if (Top is null)
@@ -174,11 +160,6 @@ public static partial class Application // Run (Begin, Run, End, Stop)
             toplevel.EndInit (); // Calls Layout
             toplevel.EndInit (); // Calls Layout
         }
         }
 
 
-        // Call ConfigurationManager Apply here to ensure all subscribers to ConfigurationManager.Applied
-        // can update their state appropriately.
-        // BUGBUG: DO NOT DO THIS. Leave this commented out until we can figure out how to do this right
-        //Apply ();
-
         // Try to set initial focus to any TabStop
         // Try to set initial focus to any TabStop
         if (!toplevel.HasFocus)
         if (!toplevel.HasFocus)
         {
         {
@@ -187,7 +168,7 @@ public static partial class Application // Run (Begin, Run, End, Stop)
 
 
         toplevel.OnLoaded ();
         toplevel.OnLoaded ();
 
 
-        LayoutAndDraw (true);
+        ApplicationImpl.Instance.LayoutAndDraw (true);
 
 
         if (PositionCursor ())
         if (PositionCursor ())
         {
         {
@@ -402,44 +383,19 @@ public static partial class Application // Run (Begin, Run, End, Stop)
     ///     need to be laid out (see <see cref="View.NeedsLayout"/>) will be laid out.
     ///     need to be laid out (see <see cref="View.NeedsLayout"/>) will be laid out.
     ///     Only Views that need to be drawn (see <see cref="View.NeedsDraw"/>) will be drawn.
     ///     Only Views that need to be drawn (see <see cref="View.NeedsDraw"/>) will be drawn.
     /// </summary>
     /// </summary>
-    /// <param name="forceDraw">
+    /// <param name="forceRedraw">
     ///     If <see langword="true"/> the entire View hierarchy will be redrawn. The default is <see langword="false"/> and
     ///     If <see langword="true"/> the entire View hierarchy will be redrawn. The default is <see langword="false"/> and
     ///     should only be overriden for testing.
     ///     should only be overriden for testing.
     /// </param>
     /// </param>
-    public static void LayoutAndDraw (bool forceDraw = false)
+    public static void LayoutAndDraw (bool forceRedraw = false)
     {
     {
-        List<View> tops = [.. TopLevels];
-
-        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)
-        {
-            forceDraw = true;
-            ClearScreenNextIteration = false;
-        }
-
-        if (forceDraw)
-        {
-            Driver?.ClearContents ();
-        }
-
-        View.SetClipToScreen ();
-        View.Draw (tops, neededLayout || forceDraw);
-        View.SetClipToScreen ();
-        Driver?.Refresh ();
+        ApplicationImpl.Instance.LayoutAndDraw (forceRedraw);
     }
     }
 
 
     /// <summary>This event is raised on each iteration of the main loop.</summary>
     /// <summary>This event is raised on each iteration of the main loop.</summary>
     /// <remarks>See also <see cref="Timeout"/></remarks>
     /// <remarks>See also <see cref="Timeout"/></remarks>
     public static event EventHandler<IterationEventArgs>? Iteration;
     public static event EventHandler<IterationEventArgs>? Iteration;
-    
+
     /// <summary>The <see cref="MainLoop"/> driver for the application</summary>
     /// <summary>The <see cref="MainLoop"/> driver for the application</summary>
     /// <value>The main loop.</value>
     /// <value>The main loop.</value>
     internal static MainLoop? MainLoop { get; set; }
     internal static MainLoop? MainLoop { get; set; }
@@ -492,35 +448,10 @@ public static partial class Application // Run (Begin, Run, End, Stop)
     /// <returns><see langword="false"/> if at least one iteration happened.</returns>
     /// <returns><see langword="false"/> if at least one iteration happened.</returns>
     public static bool RunIteration (ref RunState state, bool firstIteration = false)
     public static bool RunIteration (ref RunState state, bool firstIteration = false)
     {
     {
-        // If the driver has events pending do an iteration of the driver MainLoop
-        if (MainLoop is { Running: true } && MainLoop.EventsPending ())
-        {
-            // Notify Toplevel it's ready
-            if (firstIteration)
-            {
-                state.Toplevel.OnReady ();
-            }
+        ApplicationImpl appImpl = (ApplicationImpl)ApplicationImpl.Instance;
+        appImpl.Coordinator?.RunIteration ();
 
 
-            MainLoop.RunIteration ();
-
-            Iteration?.Invoke (null, new ());
-        }
-
-        firstIteration = false;
-
-        if (Top is null)
-        {
-            return firstIteration;
-        }
-
-        LayoutAndDraw (TopLevels.Any (v => v.NeedsLayout || v.NeedsDraw));
-
-        if (PositionCursor ())
-        {
-            Driver?.UpdateCursor ();
-        }
-
-        return firstIteration;
+        return false;
     }
     }
 
 
     /// <summary>Stops the provided <see cref="Toplevel"/>, causing or the <paramref name="top"/> if provided.</summary>
     /// <summary>Stops the provided <see cref="Toplevel"/>, causing or the <paramref name="top"/> if provided.</summary>
@@ -535,14 +466,6 @@ public static partial class Application // Run (Begin, Run, End, Stop)
     /// </remarks>
     /// </remarks>
     public static void RequestStop (Toplevel? top = null) { ApplicationImpl.Instance.RequestStop (top); }
     public static void RequestStop (Toplevel? top = null) { ApplicationImpl.Instance.RequestStop (top); }
 
 
-    internal static void OnNotifyStopRunState (Toplevel top)
-    {
-        if (EndAfterFirstIteration)
-        {
-            NotifyStopRunState?.Invoke (top, new (top));
-        }
-    }
-
     /// <summary>
     /// <summary>
     ///     Building block API: completes the execution of a <see cref="Toplevel"/> that was started with
     ///     Building block API: completes the execution of a <see cref="Toplevel"/> that was started with
     ///     <see cref="Begin(Toplevel)"/> .
     ///     <see cref="Begin(Toplevel)"/> .

+ 8 - 29
Terminal.Gui/App/Application.Screen.cs

@@ -2,11 +2,8 @@
 
 
 namespace Terminal.Gui.App;
 namespace Terminal.Gui.App;
 
 
-public static partial class Application // Screen related stuff
+public static partial class Application // Screen related stuff; intended to hide Driver details
 {
 {
-    private static readonly object _lockScreen = new ();
-    private static Rectangle? _screen;
-
     /// <summary>
     /// <summary>
     ///     Gets or sets the size of the screen. By default, this is the size of the screen as reported by the <see cref="IConsoleDriver"/>.
     ///     Gets or sets the size of the screen. By default, this is the size of the screen as reported by the <see cref="IConsoleDriver"/>.
     /// </summary>
     /// </summary>
@@ -17,30 +14,8 @@ public static partial class Application // Screen related stuff
     /// </remarks>
     /// </remarks>
     public static Rectangle Screen
     public static Rectangle Screen
     {
     {
-        get
-        {
-            lock (_lockScreen)
-            {
-                if (_screen == null)
-                {
-                    _screen = Driver?.Screen ?? new (new (0, 0), new (2048, 2048));
-                }
-
-                return _screen.Value;
-            }
-        }
-        set
-        {
-            if (value is {} && (value.X != 0 || value.Y != 0))
-            {
-                throw new NotImplementedException ($"Screen locations other than 0, 0 are not yet supported");
-            }
-
-            lock (_lockScreen)
-            {
-                _screen = value;
-            }
-        }
+        get => ApplicationImpl.Instance.Screen;
+        set => ApplicationImpl.Instance.Screen = value;
     }
     }
 
 
     /// <summary>Invoked when the terminal's size changed. The new size of the terminal is provided.</summary>
     /// <summary>Invoked when the terminal's size changed. The new size of the terminal is provided.</summary>
@@ -85,5 +60,9 @@ public static partial class Application // Screen related stuff
     ///     This is typical set to true when a View's <see cref="View.Frame"/> changes and that view has no
     ///     This is typical set to true when a View's <see cref="View.Frame"/> changes and that view has no
     ///     SuperView (e.g. when <see cref="Application.Top"/> is moved or resized.
     ///     SuperView (e.g. when <see cref="Application.Top"/> is moved or resized.
     /// </remarks>
     /// </remarks>
-    internal static bool ClearScreenNextIteration { get; set; }
+    internal static bool ClearScreenNextIteration
+    {
+        get => ApplicationImpl.Instance.ClearScreenNextIteration;
+        set => ApplicationImpl.Instance.ClearScreenNextIteration = value;
+    }
 }
 }

+ 6 - 14
Terminal.Gui/App/Application.Toplevel.cs

@@ -7,22 +7,14 @@ public static partial class Application // Toplevel handling
 {
 {
     // BUGBUG: Technically, this is not the full lst of TopLevels. There be dragons here, e.g. see how Toplevel.Id is used. What
     // BUGBUG: Technically, this is not the full lst of TopLevels. There be dragons here, e.g. see how Toplevel.Id is used. What
 
 
-    private static readonly ConcurrentStack<Toplevel> _topLevels = new ();
-    private static readonly object _topLevelsLock = new ();
-
     /// <summary>Holds the stack of TopLevel views.</summary>
     /// <summary>Holds the stack of TopLevel views.</summary>
-    internal static ConcurrentStack<Toplevel> TopLevels
-    {
-        get
-        {
-            lock (_topLevelsLock)
-            {
-                return _topLevels;
-            }
-        }
-    }
+    internal static ConcurrentStack<Toplevel> TopLevels => ApplicationImpl.Instance.TopLevels;
 
 
     /// <summary>The <see cref="Toplevel"/> that is currently active.</summary>
     /// <summary>The <see cref="Toplevel"/> that is currently active.</summary>
     /// <value>The top.</value>
     /// <value>The top.</value>
-    public static Toplevel? Top { get; internal set; }
+    public static Toplevel? Top
+    {
+        get => ApplicationImpl.Instance.Top;
+        internal set => ApplicationImpl.Instance.Top = value;
+    }
 }
 }

+ 10 - 5
Terminal.Gui/App/Application.cs

@@ -232,7 +232,13 @@ public static partial class Application
             Driver = null;
             Driver = null;
         }
         }
 
 
-        _screen = null;
+        // Reset Screen to null so it will be recalculated on next access
+        // Note: ApplicationImpl.Shutdown() also calls ResetScreen() before calling this method
+        // to avoid potential circular reference issues. Calling it twice is harmless.
+        if (ApplicationImpl.Instance is ApplicationImpl impl)
+        {
+            impl.ResetScreen ();
+        }
 
 
         // Don't reset ForceDriver; it needs to be set before Init is called.
         // Don't reset ForceDriver; it needs to be set before Init is called.
         //ForceDriver = string.Empty;
         //ForceDriver = string.Empty;
@@ -242,16 +248,15 @@ public static partial class Application
         // Run State stuff
         // Run State stuff
         NotifyNewRunState = null;
         NotifyNewRunState = null;
         NotifyStopRunState = null;
         NotifyStopRunState = null;
-        MouseGrabHandler = new MouseGrabHandler ();
-        // Keyboard will be lazy-initialized in ApplicationImpl on next access
+        // Mouse and Keyboard will be lazy-initialized in ApplicationImpl on next access
         Initialized = false;
         Initialized = false;
 
 
         // Mouse
         // Mouse
-        // Do not clear _lastMousePosition; Popover's require it to stay set with
+        // Do not clear _lastMousePosition; Popovers require it to stay set with
         // last mouse pos.
         // last mouse pos.
         //_lastMousePosition = null;
         //_lastMousePosition = null;
         CachedViewsUnderMouse.Clear ();
         CachedViewsUnderMouse.Clear ();
-        MouseEvent = null;
+        ResetMouseState ();
 
 
         // Keyboard events and bindings are now managed by the Keyboard instance
         // Keyboard events and bindings are now managed by the Keyboard instance
 
 

+ 187 - 40
Terminal.Gui/App/ApplicationImpl.cs

@@ -17,6 +17,19 @@ public class ApplicationImpl : IApplication
     private IMainLoopCoordinator? _coordinator;
     private IMainLoopCoordinator? _coordinator;
     private string? _driverName;
     private string? _driverName;
     private readonly ITimedEvents _timedEvents = new TimedEvents ();
     private readonly ITimedEvents _timedEvents = new TimedEvents ();
+    private IConsoleDriver? _driver;
+    private bool _initialized;
+    private ApplicationPopover? _popover;
+    private ApplicationNavigation? _navigation;
+    private Toplevel? _top;
+    private readonly ConcurrentStack<Toplevel> _topLevels = new ();
+    private int _mainThreadId = -1;
+    private bool _force16Colors;
+    private string _forceDriver = string.Empty;
+    private readonly List<SixelToRender> _sixel = new ();
+    private readonly object _lockScreen = new ();
+    private Rectangle? _screen;
+    private bool _clearScreenNextIteration;
 
 
     // Private static readonly Lazy instance of Application
     // Private static readonly Lazy instance of Application
     private static Lazy<IApplication> _lazyInstance = new (() => new ApplicationImpl ());
     private static Lazy<IApplication> _lazyInstance = new (() => new ApplicationImpl ());
@@ -32,6 +45,24 @@ public class ApplicationImpl : IApplication
 
 
     internal IMainLoopCoordinator? Coordinator => _coordinator;
     internal IMainLoopCoordinator? Coordinator => _coordinator;
 
 
+    private IMouse? _mouse;
+
+    /// <summary>
+    /// Handles mouse event state and processing.
+    /// </summary>
+    public IMouse Mouse
+    {
+        get
+        {
+            if (_mouse is null)
+            {
+                _mouse = new MouseImpl { Application = this };
+            }
+            return _mouse;
+        }
+        set => _mouse = value ?? throw new ArgumentNullException (nameof (value));
+    }
+
     /// <summary>
     /// <summary>
     /// Handles which <see cref="View"/> (if any) has captured the mouse
     /// Handles which <see cref="View"/> (if any) has captured the mouse
     /// </summary>
     /// </summary>
@@ -48,7 +79,7 @@ public class ApplicationImpl : IApplication
         {
         {
             if (_keyboard is null)
             if (_keyboard is null)
             {
             {
-                _keyboard = new Keyboard { Application = this };
+                _keyboard = new KeyboardImpl { Application = this };
             }
             }
             return _keyboard;
             return _keyboard;
         }
         }
@@ -58,43 +89,105 @@ public class ApplicationImpl : IApplication
     /// <inheritdoc/>
     /// <inheritdoc/>
     public IConsoleDriver? Driver
     public IConsoleDriver? Driver
     {
     {
-        get => Application.Driver;
-        set => Application.Driver = value;
+        get => _driver;
+        set => _driver = value;
     }
     }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
     public bool Initialized
     public bool Initialized
     {
     {
-        get => Application.Initialized;
-        set => Application.Initialized = value;
+        get => _initialized;
+        set => _initialized = value;
+    }
+
+    /// <inheritdoc/>
+    public bool Force16Colors
+    {
+        get => _force16Colors;
+        set => _force16Colors = value;
+    }
+
+    /// <inheritdoc/>
+    public string ForceDriver
+    {
+        get => _forceDriver;
+        set => _forceDriver = value;
+    }
+
+    /// <inheritdoc/>
+    public List<SixelToRender> Sixel => _sixel;
+
+    /// <inheritdoc/>
+    public Rectangle Screen
+    {
+        get
+        {
+            lock (_lockScreen)
+            {
+                if (_screen == null)
+                {
+                    _screen = Driver?.Screen ?? new (new (0, 0), new (2048, 2048));
+                }
+
+                return _screen.Value;
+            }
+        }
+        set
+        {
+            if (value is {} && (value.X != 0 || value.Y != 0))
+            {
+                throw new NotImplementedException ($"Screen locations other than 0, 0 are not yet supported");
+            }
+
+            lock (_lockScreen)
+            {
+                _screen = value;
+            }
+        }
+    }
+
+    /// <inheritdoc/>
+    public bool ClearScreenNextIteration
+    {
+        get => _clearScreenNextIteration;
+        set => _clearScreenNextIteration = value;
     }
     }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
     public ApplicationPopover? Popover
     public ApplicationPopover? Popover
     {
     {
-        get => Application.Popover;
-        set => Application.Popover = value;
+        get => _popover;
+        set => _popover = value;
     }
     }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
     public ApplicationNavigation? Navigation
     public ApplicationNavigation? Navigation
     {
     {
-        get => Application.Navigation;
-        set => Application.Navigation = value;
+        get => _navigation;
+        set => _navigation = value;
     }
     }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
     public Toplevel? Top
     public Toplevel? Top
     {
     {
-        get => Application.Top;
-        set => Application.Top = value;
+        get => _top;
+        set => _top = value;
     }
     }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public ConcurrentStack<Toplevel> TopLevels => Application.TopLevels;
+    public ConcurrentStack<Toplevel> TopLevels => _topLevels;
+
+    /// <summary>
+    /// Gets or sets the main thread ID for the application.
+    /// </summary>
+    internal int MainThreadId
+    {
+        get => _mainThreadId;
+        set => _mainThreadId = value;
+    }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public void RequestStop () => Application.RequestStop ();
+    public void RequestStop () => RequestStop (null);
 
 
     /// <summary>
     /// <summary>
     /// Creates a new instance of the Application backend.
     /// Creates a new instance of the Application backend.
@@ -124,7 +217,7 @@ public class ApplicationImpl : IApplication
     [RequiresDynamicCode ("AOT")]
     [RequiresDynamicCode ("AOT")]
     public void Init (IConsoleDriver? driver = null, string? driverName = null)
     public void Init (IConsoleDriver? driver = null, string? driverName = null)
     {
     {
-        if (Application.Initialized)
+        if (_initialized)
         {
         {
             Logging.Logger.LogError ("Init called multiple times without shutdown, aborting.");
             Logging.Logger.LogError ("Init called multiple times without shutdown, aborting.");
 
 
@@ -141,11 +234,11 @@ public class ApplicationImpl : IApplication
             _driverName = Application.ForceDriver;
             _driverName = Application.ForceDriver;
         }
         }
 
 
-        Debug.Assert(Application.Navigation is null);
-        Application.Navigation = new ();
+        Debug.Assert(_navigation is null);
+        _navigation = new ();
 
 
-        Debug.Assert (Application.Popover is null);
-        Application.Popover = new ();
+        Debug.Assert (_popover is null);
+        _popover = new ();
 
 
         // Preserve existing keyboard settings if they exist
         // Preserve existing keyboard settings if they exist
         bool hasExistingKeyboard = _keyboard is not null;
         bool hasExistingKeyboard = _keyboard is not null;
@@ -157,7 +250,7 @@ public class ApplicationImpl : IApplication
         Key existingPrevTabGroupKey = _keyboard?.PrevTabGroupKey ?? Key.F6.WithShift;
         Key existingPrevTabGroupKey = _keyboard?.PrevTabGroupKey ?? Key.F6.WithShift;
 
 
         // Reset keyboard to ensure fresh state with default bindings
         // Reset keyboard to ensure fresh state with default bindings
-        _keyboard = new Keyboard { Application = this };
+        _keyboard = new KeyboardImpl { Application = this };
 
 
         // Restore previously set keys if they existed and were different from defaults
         // Restore previously set keys if they existed and were different from defaults
         if (hasExistingKeyboard)
         if (hasExistingKeyboard)
@@ -172,13 +265,13 @@ public class ApplicationImpl : IApplication
 
 
         CreateDriver (driverName ?? _driverName);
         CreateDriver (driverName ?? _driverName);
 
 
-        Application.Initialized = true;
+        _initialized = true;
 
 
         Application.OnInitializedChanged (this, new (true));
         Application.OnInitializedChanged (this, new (true));
         Application.SubscribeDriverEvents ();
         Application.SubscribeDriverEvents ();
 
 
         SynchronizationContext.SetSynchronizationContext (new ());
         SynchronizationContext.SetSynchronizationContext (new ());
-        Application.MainThreadId = Thread.CurrentThread.ManagedThreadId;
+        _mainThreadId = Thread.CurrentThread.ManagedThreadId;
     }
     }
 
 
     private void CreateDriver (string? driverName)
     private void CreateDriver (string? driverName)
@@ -192,9 +285,9 @@ public class ApplicationImpl : IApplication
             _coordinator = CreateSubcomponents (() => new FakeComponentFactory ());
             _coordinator = CreateSubcomponents (() => new FakeComponentFactory ());
             _coordinator.StartAsync ().Wait ();
             _coordinator.StartAsync ().Wait ();
 
 
-            if (Application.Driver == null)
+            if (_driver == null)
             {
             {
-                throw new ("Application.Driver was null even after booting MainLoopCoordinator");
+                throw new ("Driver was null even after booting MainLoopCoordinator");
             }
             }
 
 
             return;
             return;
@@ -242,9 +335,9 @@ public class ApplicationImpl : IApplication
 
 
         _coordinator.StartAsync ().Wait ();
         _coordinator.StartAsync ().Wait ();
 
 
-        if (Application.Driver == null)
+        if (_driver == null)
         {
         {
-            throw new ("Application.Driver was null even after booting MainLoopCoordinator");
+            throw new ("Driver was null even after booting MainLoopCoordinator");
         }
         }
     }
     }
 
 
@@ -291,7 +384,7 @@ public class ApplicationImpl : IApplication
     public T Run<T> (Func<Exception, bool>? errorHandler = null, IConsoleDriver? driver = null)
     public T Run<T> (Func<Exception, bool>? errorHandler = null, IConsoleDriver? driver = null)
         where T : Toplevel, new()
         where T : Toplevel, new()
     {
     {
-        if (!Application.Initialized)
+        if (!_initialized)
         {
         {
             // Init() has NOT been called. Auto-initialize as per interface contract.
             // Init() has NOT been called. Auto-initialize as per interface contract.
             Init (driver, null);
             Init (driver, null);
@@ -310,23 +403,23 @@ public class ApplicationImpl : IApplication
         Logging.Information ($"Run '{view}'");
         Logging.Information ($"Run '{view}'");
         ArgumentNullException.ThrowIfNull (view);
         ArgumentNullException.ThrowIfNull (view);
 
 
-        if (!Application.Initialized)
+        if (!_initialized)
         {
         {
             throw new NotInitializedException (nameof (Run));
             throw new NotInitializedException (nameof (Run));
         }
         }
 
 
-        if (Application.Driver == null)
+        if (_driver == null)
         {
         {
             throw new  InvalidOperationException ("Driver was inexplicably null when trying to Run view");
             throw new  InvalidOperationException ("Driver was inexplicably null when trying to Run view");
         }
         }
 
 
-        Application.Top = view;
+        _top = view;
 
 
         RunState rs = Application.Begin (view);
         RunState rs = Application.Begin (view);
 
 
-        Application.Top.Running = true;
+        _top.Running = true;
 
 
-        while (Application.TopLevels.TryPeek (out Toplevel? found) && found == view && view.Running)
+        while (_topLevels.TryPeek (out Toplevel? found) && found == view && view.Running)
         {
         {
             if (_coordinator is null)
             if (_coordinator is null)
             {
             {
@@ -345,18 +438,37 @@ public class ApplicationImpl : IApplication
     {
     {
         _coordinator?.Stop ();
         _coordinator?.Stop ();
         
         
-        bool wasInitialized = Application.Initialized;
+        bool wasInitialized = _initialized;
+        
+        // Reset Screen before calling Application.ResetState to avoid circular reference
+        ResetScreen ();
+        
+        // Call ResetState FIRST so it can properly dispose Popover and other resources
+        // that are accessed via Application.* static properties that now delegate to instance fields
         Application.ResetState ();
         Application.ResetState ();
         ConfigurationManager.PrintJsonErrors ();
         ConfigurationManager.PrintJsonErrors ();
+        
+        // Clear instance fields after ResetState has disposed everything
+        _driver = null;
+        _mouse = null;
+        _keyboard = null;
+        _initialized = false;
+        _navigation = null;
+        _popover = null;
+        _top = null;
+        _topLevels.Clear ();
+        _mainThreadId = -1;
+        _screen = null;
+        _clearScreenNextIteration = false;
+        _sixel.Clear ();
+        // Don't reset ForceDriver and Force16Colors; they need to be set before Init is called
 
 
         if (wasInitialized)
         if (wasInitialized)
         {
         {
-            bool init = Application.Initialized;
+            bool init = _initialized; // Will be false after clearing fields above
             Application.OnInitializedChanged (this, new (in init));
             Application.OnInitializedChanged (this, new (in init));
         }
         }
 
 
-        Application.Driver = null;
-        _keyboard = null;
         _lazyInstance = new (() => new ApplicationImpl ());
         _lazyInstance = new (() => new ApplicationImpl ());
     }
     }
 
 
@@ -365,7 +477,7 @@ public class ApplicationImpl : IApplication
     {
     {
         Logging.Logger.LogInformation ($"RequestStop '{(top is {} ? top : "null")}'");
         Logging.Logger.LogInformation ($"RequestStop '{(top is {} ? top : "null")}'");
 
 
-        top ??= Application.Top;
+        top ??= _top;
 
 
         if (top == null)
         if (top == null)
         {
         {
@@ -387,7 +499,7 @@ public class ApplicationImpl : IApplication
     public void Invoke (Action action)
     public void Invoke (Action action)
     {
     {
         // If we are already on the main UI thread
         // If we are already on the main UI thread
-        if (Application.MainThreadId == Thread.CurrentThread.ManagedThreadId)
+        if (Application.Top is { Running: true } && _mainThreadId == Thread.CurrentThread.ManagedThreadId)
         {
         {
             action ();
             action ();
             return;
             return;
@@ -412,9 +524,44 @@ public class ApplicationImpl : IApplication
     public bool RemoveTimeout (object token) { return _timedEvents.Remove (token); }
     public bool RemoveTimeout (object token) { return _timedEvents.Remove (token); }
 
 
     /// <inheritdoc />
     /// <inheritdoc />
-    public void LayoutAndDraw (bool forceDraw)
+    public void LayoutAndDraw (bool forceRedraw = false)
     {
     {
-        Application.Top?.SetNeedsDraw();
-        Application.Top?.SetNeedsLayout ();
+        List<View> tops = [.. _topLevels];
+
+        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)
+        {
+            forceRedraw = true;
+            ClearScreenNextIteration = false;
+        }
+
+        if (forceRedraw)
+        {
+            _driver?.ClearContents ();
+        }
+
+        View.SetClipToScreen ();
+        View.Draw (tops, neededLayout || forceRedraw);
+        View.SetClipToScreen ();
+        _driver?.Refresh ();
+    }
+
+    /// <summary>
+    /// Resets the Screen field to null so it will be recalculated on next access.
+    /// </summary>
+    internal void ResetScreen ()
+    {
+        lock (_lockScreen)
+        {
+            _screen = null;
+        }
     }
     }
 }
 }

+ 98 - 58
Terminal.Gui/App/IApplication.cs

@@ -4,26 +4,28 @@ using System.Diagnostics.CodeAnalysis;
 namespace Terminal.Gui.App;
 namespace Terminal.Gui.App;
 
 
 /// <summary>
 /// <summary>
-/// Interface for instances that provide backing functionality to static
-/// gateway class <see cref="Application"/>.
+///     Interface for instances that provide backing functionality to static
+///     gateway class <see cref="Application"/>.
 /// </summary>
 /// </summary>
 public interface IApplication
 public interface IApplication
 {
 {
-    /// <summary>
-    /// Handles recurring events. These are invoked on the main UI thread - allowing for
-    /// safe updates to <see cref="View"/> instances.
-    /// </summary>
-    ITimedEvents? TimedEvents { get; }
+    /// <summary>Adds a timeout to the application.</summary>
+    /// <remarks>
+    ///     When time specified passes, the callback will be invoked. If the callback returns true, the timeout will be
+    ///     reset, repeating the invocation. If it returns false, the timeout will stop and be removed. The returned value is a
+    ///     token that can be used to stop the timeout by calling <see cref="RemoveTimeout(object)"/>.
+    /// </remarks>
+    object AddTimeout (TimeSpan time, Func<bool> callback);
 
 
     /// <summary>
     /// <summary>
-    /// Handles grabbing the mouse (only a single <see cref="View"/> can grab the mouse at once).
+    /// Handles keyboard input and key bindings at the Application level.
     /// </summary>
     /// </summary>
-    IMouseGrabHandler MouseGrabHandler { get; set; }
+    IKeyboard Keyboard { get; set; }
 
 
     /// <summary>
     /// <summary>
-    /// Handles keyboard input and key bindings at the Application level.
+    ///     Handles mouse event state and processing.
     /// </summary>
     /// </summary>
-    IKeyboard Keyboard { get; set; }
+    IMouse Mouse { get; set; }
 
 
     /// <summary>Gets or sets the console driver being used.</summary>
     /// <summary>Gets or sets the console driver being used.</summary>
     IConsoleDriver? Driver { get; set; }
     IConsoleDriver? Driver { get; set; }
@@ -31,6 +33,35 @@ public interface IApplication
     /// <summary>Gets or sets whether the application has been initialized.</summary>
     /// <summary>Gets or sets whether the application has been initialized.</summary>
     bool Initialized { get; set; }
     bool Initialized { get; set; }
 
 
+    /// <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="IConsoleDriver"/> supports TrueColor.
+    /// </summary>
+    bool Force16Colors { get; set; }
+
+    /// <summary>
+    ///     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.
+    /// </summary>
+    string ForceDriver { 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; }
+
+    /// <summary>
+    ///     Gets or sets the size of the screen. By default, this is the size of the screen as reported by the <see cref="IConsoleDriver"/>.
+    /// </summary>
+    Rectangle Screen { get; set; }
+
+    /// <summary>
+    ///     Gets or sets whether the screen will be cleared, and all Views redrawn, during the next Application iteration.
+    /// </summary>
+    bool ClearScreenNextIteration { get; set; }
+
     /// <summary>Gets or sets the popover manager.</summary>
     /// <summary>Gets or sets the popover manager.</summary>
     ApplicationPopover? Popover { get; set; }
     ApplicationPopover? Popover { get; set; }
 
 
@@ -46,9 +77,16 @@ public interface IApplication
     /// <summary>Requests that the application stop running.</summary>
     /// <summary>Requests that the application stop running.</summary>
     void RequestStop ();
     void RequestStop ();
 
 
-    /// <summary>Forces all views to be laid out and drawn.</summary>
-    /// <param name="clearScreen">If true, clears the screen before drawing.</param>
-    void LayoutAndDraw (bool clearScreen = false);
+    /// <summary>
+    ///     Causes any Toplevels that need layout to be laid out. Then draws any Toplevels that need display. Only Views that
+    ///     need to be laid out (see <see cref="View.NeedsLayout"/>) will be laid out.
+    ///     Only Views that need to be drawn (see <see cref="View.NeedsDraw"/>) will be drawn.
+    /// </summary>
+    /// <param name="forceRedraw">
+    ///     If <see langword="true"/> the entire View hierarchy will be redrawn. The default is <see langword="false"/> and
+    ///     should only be overriden for testing.
+    /// </param>
+    public void LayoutAndDraw (bool forceRedraw = false);
 
 
     /// <summary>Initializes a new instance of <see cref="Terminal.Gui"/> Application.</summary>
     /// <summary>Initializes a new instance of <see cref="Terminal.Gui"/> Application.</summary>
     /// <para>Call this method once per instance (or after <see cref="Shutdown"/> has been called).</para>
     /// <para>Call this method once per instance (or after <see cref="Shutdown"/> has been called).</para>
@@ -82,6 +120,39 @@ public interface IApplication
     [RequiresDynamicCode ("AOT")]
     [RequiresDynamicCode ("AOT")]
     public void Init (IConsoleDriver? driver = null, string? driverName = null);
     public void Init (IConsoleDriver? driver = null, string? driverName = null);
 
 
+    /// <summary>Runs <paramref name="action"/> on the main UI loop thread</summary>
+    /// <param name="action">the action to be invoked on the main processing thread.</param>
+    void Invoke (Action action);
+
+    /// <summary>
+    ///     <see langword="true"/> if implementation is 'old'. <see langword="false"/> if implementation
+    ///     is cutting edge.
+    /// </summary>
+    bool IsLegacy { get; }
+
+    /// <summary>Removes a previously scheduled timeout</summary>
+    /// <remarks>The token parameter is the value returned by <see cref="AddTimeout"/>.</remarks>
+    /// <returns>
+    ///     <see langword="true"/>
+    ///     if the timeout is successfully removed; otherwise,
+    ///     <see langword="false"/>
+    ///     .
+    ///     This method also returns
+    ///     <see langword="false"/>
+    ///     if the timeout is not found.
+    /// </returns>
+    bool RemoveTimeout (object token);
+
+    /// <summary>Stops the provided <see cref="Toplevel"/>, causing or the <paramref name="top"/> if provided.</summary>
+    /// <param name="top">The <see cref="Toplevel"/> to stop.</param>
+    /// <remarks>
+    ///     <para>This will cause <see cref="Application.Run(Toplevel, Func{Exception, bool})"/> to return.</para>
+    ///     <para>
+    ///         Calling <see cref="RequestStop(Toplevel)"/> is equivalent to setting the <see cref="Toplevel.Running"/>
+    ///         property on the currently running <see cref="Toplevel"/> to false.
+    ///     </para>
+    /// </remarks>
+    void RequestStop (Toplevel? top);
 
 
     /// <summary>
     /// <summary>
     ///     Runs the application by creating a <see cref="Toplevel"/> object and calling
     ///     Runs the application by creating a <see cref="Toplevel"/> object and calling
@@ -126,7 +197,7 @@ public interface IApplication
     [RequiresUnreferencedCode ("AOT")]
     [RequiresUnreferencedCode ("AOT")]
     [RequiresDynamicCode ("AOT")]
     [RequiresDynamicCode ("AOT")]
     public T Run<T> (Func<Exception, bool>? errorHandler = null, IConsoleDriver? driver = null)
     public T Run<T> (Func<Exception, bool>? errorHandler = null, IConsoleDriver? driver = null)
-        where T : Toplevel, new ();
+        where T : Toplevel, new();
 
 
     /// <summary>Runs the Application using the provided <see cref="Toplevel"/> view.</summary>
     /// <summary>Runs the Application using the provided <see cref="Toplevel"/> view.</summary>
     /// <remarks>
     /// <remarks>
@@ -140,24 +211,28 @@ public interface IApplication
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
     ///         Calling <see cref="Run(Toplevel,System.Func{System.Exception,bool})"/> is equivalent to calling
     ///         Calling <see cref="Run(Toplevel,System.Func{System.Exception,bool})"/> is equivalent to calling
-    ///         <see cref="Application.Begin(Toplevel)"/>, followed by <see cref="Application.RunLoop(RunState)"/>, and then calling
+    ///         <see cref="Application.Begin(Toplevel)"/>, followed by <see cref="Application.RunLoop(RunState)"/>, and then
+    ///         calling
     ///         <see cref="Application.End(RunState)"/>.
     ///         <see cref="Application.End(RunState)"/>.
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
     ///         Alternatively, to have a program control the main loop and process events manually, call
     ///         Alternatively, to have a program control the main loop and process events manually, call
     ///         <see cref="Application.Begin(Toplevel)"/> to set things up manually and then repeatedly call
     ///         <see cref="Application.Begin(Toplevel)"/> to set things up manually and then repeatedly call
     ///         <see cref="Application.RunLoop(RunState)"/> with the wait parameter set to false. By doing this the
     ///         <see cref="Application.RunLoop(RunState)"/> with the wait parameter set to false. By doing this the
-    ///         <see cref="Application.RunLoop(RunState)"/> method will only process any pending events, timers handlers and then
+    ///         <see cref="Application.RunLoop(RunState)"/> method will only process any pending events, timers handlers and
+    ///         then
     ///         return control immediately.
     ///         return control immediately.
     ///     </para>
     ///     </para>
-    ///     <para>When using <see cref="Run{T}"/> or
+    ///     <para>
+    ///         When using <see cref="Run{T}"/> or
     ///         <see cref="Run(System.Func{System.Exception,bool},IConsoleDriver)"/>
     ///         <see cref="Run(System.Func{System.Exception,bool},IConsoleDriver)"/>
     ///         <see cref="Init"/> will be called automatically.
     ///         <see cref="Init"/> will be called automatically.
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
     ///         RELEASE builds only: When <paramref name="errorHandler"/> is <see langword="null"/> any exceptions will be
     ///         RELEASE builds only: When <paramref name="errorHandler"/> is <see langword="null"/> any exceptions will be
     ///         rethrown. Otherwise, if <paramref name="errorHandler"/> will be called. If <paramref name="errorHandler"/>
     ///         rethrown. Otherwise, if <paramref name="errorHandler"/> will be called. If <paramref name="errorHandler"/>
-    ///         returns <see langword="true"/> the <see cref="Application.RunLoop(RunState)"/> will resume; otherwise this method will
+    ///         returns <see langword="true"/> the <see cref="Application.RunLoop(RunState)"/> will resume; otherwise this
+    ///         method will
     ///         exit.
     ///         exit.
     ///     </para>
     ///     </para>
     /// </remarks>
     /// </remarks>
@@ -177,44 +252,9 @@ public interface IApplication
     /// </remarks>
     /// </remarks>
     public void Shutdown ();
     public void Shutdown ();
 
 
-    /// <summary>Stops the provided <see cref="Toplevel"/>, causing or the <paramref name="top"/> if provided.</summary>
-    /// <param name="top">The <see cref="Toplevel"/> to stop.</param>
-    /// <remarks>
-    ///     <para>This will cause <see cref="Application.Run(Toplevel, Func{Exception, bool})"/> to return.</para>
-    ///     <para>
-    ///         Calling <see cref="RequestStop(Toplevel)"/> is equivalent to setting the <see cref="Toplevel.Running"/>
-    ///         property on the currently running <see cref="Toplevel"/> to false.
-    ///     </para>
-    /// </remarks>
-    void RequestStop (Toplevel? top);
-
-    /// <summary>Runs <paramref name="action"/> on the main UI loop thread</summary>
-    /// <param name="action">the action to be invoked on the main processing thread.</param>
-    void Invoke (Action action);
-
     /// <summary>
     /// <summary>
-    /// <see langword="true"/> if implementation is 'old'. <see langword="false"/> if implementation
-    /// is cutting edge.
+    ///     Handles recurring events. These are invoked on the main UI thread - allowing for
+    ///     safe updates to <see cref="View"/> instances.
     /// </summary>
     /// </summary>
-    bool IsLegacy { get; }
-    
-    /// <summary>Adds a timeout to the application.</summary>
-    /// <remarks>
-    ///     When time specified passes, the callback will be invoked. If the callback returns true, the timeout will be
-    ///     reset, repeating the invocation. If it returns false, the timeout will stop and be removed. The returned value is a
-    ///     token that can be used to stop the timeout by calling <see cref="RemoveTimeout(object)"/>.
-    /// </remarks>
-    object AddTimeout (TimeSpan time, Func<bool> callback);
-
-    /// <summary>Removes a previously scheduled timeout</summary>
-    /// <remarks>The token parameter is the value returned by <see cref="AddTimeout"/>.</remarks>
-    /// <returns>
-    /// <see langword="true"/>
-    /// if the timeout is successfully removed; otherwise,
-    /// <see langword="false"/>
-    /// .
-    /// This method also returns
-    /// <see langword="false"/>
-    /// if the timeout is not found.</returns>
-    bool RemoveTimeout (object token);
-}
+    ITimedEvents? TimedEvents { get; }
+}

+ 2 - 2
Terminal.Gui/App/Keyboard/Keyboard.cs → Terminal.Gui/App/Keyboard/KeyboardImpl.cs

@@ -11,7 +11,7 @@ namespace Terminal.Gui.App;
 ///         See <see cref="IKeyboard"/> for usage details.
 ///         See <see cref="IKeyboard"/> for usage details.
 ///     </para>
 ///     </para>
 /// </summary>
 /// </summary>
-internal class Keyboard : IKeyboard
+internal class KeyboardImpl : IKeyboard
 {
 {
     private Key _quitKey = Key.Esc; // Resources/config.json overrides
     private Key _quitKey = Key.Esc; // Resources/config.json overrides
     private Key _arrangeKey = Key.F5.WithCtrl; // Resources/config.json overrides
     private Key _arrangeKey = Key.F5.WithCtrl; // Resources/config.json overrides
@@ -106,7 +106,7 @@ internal class Keyboard : IKeyboard
     /// <summary>
     /// <summary>
     ///     Initializes keyboard bindings.
     ///     Initializes keyboard bindings.
     /// </summary>
     /// </summary>
-    public Keyboard ()
+    public KeyboardImpl ()
     {
     {
         AddKeyBindings ();
         AddKeyBindings ();
     }
     }

+ 1 - 1
Terminal.Gui/App/MainLoop/ApplicationMainLoop.cs

@@ -150,7 +150,7 @@ public class ApplicationMainLoop<T> : IApplicationMainLoop<T>
         {
         {
             bool needsDrawOrLayout = AnySubViewsNeedDrawn (Application.Popover?.GetActivePopover () as View)
             bool needsDrawOrLayout = AnySubViewsNeedDrawn (Application.Popover?.GetActivePopover () as View)
                                      || AnySubViewsNeedDrawn (Application.Top)
                                      || AnySubViewsNeedDrawn (Application.Top)
-                                     || (Application.MouseGrabHandler.MouseGrabView != null && AnySubViewsNeedDrawn (Application.MouseGrabHandler.MouseGrabView));
+                                     || (Application.Mouse.MouseGrabView != null && AnySubViewsNeedDrawn (Application.Mouse.MouseGrabView));
 
 
             bool sizeChanged = WindowSizeMonitor.Poll ();
             bool sizeChanged = WindowSizeMonitor.Poll ();
 
 

+ 79 - 0
Terminal.Gui/App/Mouse/IMouse.cs

@@ -0,0 +1,79 @@
+#nullable enable
+using System.ComponentModel;
+
+namespace Terminal.Gui.App;
+
+/// <summary>
+///     Defines a contract for mouse event handling and state management in a Terminal.Gui application.
+///     <para>
+///         This interface allows for decoupling of mouse-related functionality from the static <see cref="Application"/> class,
+///         enabling better testability and parallel test execution.
+///     </para>
+/// </summary>
+public interface IMouse : IMouseGrabHandler
+{
+    /// <summary>
+    /// Sets the application instance that this mouse handler is associated with.
+    /// This provides access to application state without coupling to static Application class.
+    /// </summary>
+    IApplication? Application { get; set; }
+
+    /// <summary>
+    ///     Gets or sets the last known position of the mouse.
+    /// </summary>
+    Point? LastMousePosition { get; set; }
+
+    /// <summary>
+    ///     Gets the most recent position of the mouse.
+    /// </summary>
+    Point? GetLastMousePosition ();
+
+    /// <summary>
+    ///     Gets or sets whether the mouse is disabled. The mouse is enabled by default.
+    /// </summary>
+    bool IsMouseDisabled { get; set; }
+
+    /// <summary>
+    ///     Gets the list of non-<see cref="ViewportSettingsFlags.TransparentMouse"/> views that are currently under the mouse.
+    /// </summary>
+    List<View?> CachedViewsUnderMouse { get; }
+
+    /// <summary>
+    ///     Raised when a mouse event occurs. Can be cancelled by setting <see cref="HandledEventArgs.Handled"/> to <see langword="true"/>.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         <see cref="MouseEventArgs.ScreenPosition"/> coordinates are screen-relative.
+    ///     </para>
+    ///     <para>
+    ///         <see cref="MouseEventArgs.View"/> will be the deepest view under the mouse.
+    ///     </para>
+    ///     <para>
+    ///         <see cref="MouseEventArgs.Position"/> coordinates are view-relative. Only valid if <see cref="MouseEventArgs.View"/> is set.
+    ///     </para>
+    ///     <para>
+    ///         Use this even to handle mouse events at the application level, before View-specific handling.
+    ///     </para>
+    /// </remarks>
+    event EventHandler<MouseEventArgs>? MouseEvent;
+
+    /// <summary>
+    ///     INTERNAL API: Called when a mouse event is raised by the driver. Determines the view under the mouse and
+    ///     calls the appropriate View mouse event handlers.
+    /// </summary>
+    /// <remarks>This method can be used to simulate a mouse event, e.g. in unit tests.</remarks>
+    /// <param name="mouseEvent">The mouse event with coordinates relative to the screen.</param>
+    void RaiseMouseEvent (MouseEventArgs mouseEvent);
+
+    /// <summary>
+    ///     INTERNAL: Raises the MouseEnter and MouseLeave events for the views that are under the mouse.
+    /// </summary>
+    /// <param name="screenPosition">The position of the mouse.</param>
+    /// <param name="currentViewsUnderMouse">The most recent result from GetViewsUnderLocation().</param>
+    void RaiseMouseEnterLeaveEvents (Point screenPosition, List<View?> currentViewsUnderMouse);
+
+    /// <summary>
+    ///     INTERNAL: Resets mouse state, clearing event handlers and cached views.
+    /// </summary>
+    void ResetState ();
+}

+ 8 - 0
Terminal.Gui/App/Mouse/IMouseGrabHandler.cs

@@ -84,4 +84,12 @@ public interface IMouseGrabHandler
     ///     Releases the mouse grab, so mouse events will be routed to the view under the mouse pointer.
     ///     Releases the mouse grab, so mouse events will be routed to the view under the mouse pointer.
     /// </summary>
     /// </summary>
     public void UngrabMouse ();
     public void UngrabMouse ();
+
+    /// <summary>
+    ///     Handles mouse grab logic for a mouse event.
+    /// </summary>
+    /// <param name="deepestViewUnderMouse">The deepest view under the mouse.</param>
+    /// <param name="mouseEvent">The mouse event to handle.</param>
+    /// <returns><see langword="true"/> if the event was handled by the grab handler; otherwise <see langword="false"/>.</returns>
+    bool HandleMouseGrab (View? deepestViewUnderMouse, MouseEventArgs mouseEvent);
 }
 }

+ 41 - 0
Terminal.Gui/App/Mouse/MouseGrabHandler.cs

@@ -115,4 +115,45 @@ internal class MouseGrabHandler : IMouseGrabHandler
 
 
         UnGrabbedMouse?.Invoke (view, new (view));
         UnGrabbedMouse?.Invoke (view, new (view));
     }
     }
+
+    /// <inheritdoc/>
+    public bool HandleMouseGrab (View? deepestViewUnderMouse, MouseEventArgs mouseEvent)
+    {
+        if (MouseGrabView is { })
+        {
+#if DEBUG_IDISPOSABLE
+            if (View.EnableDebugIDisposableAsserts && MouseGrabView.WasDisposed)
+            {
+                throw new ObjectDisposedException (MouseGrabView.GetType ().FullName);
+            }
+#endif
+
+            // If the mouse is grabbed, send the event to the view that grabbed it.
+            // The coordinates are relative to the Bounds of the view that grabbed the mouse.
+            Point frameLoc = MouseGrabView.ScreenToViewport (mouseEvent.ScreenPosition);
+
+            var viewRelativeMouseEvent = new MouseEventArgs
+            {
+                Position = frameLoc,
+                Flags = mouseEvent.Flags,
+                ScreenPosition = mouseEvent.ScreenPosition,
+                View = deepestViewUnderMouse ?? MouseGrabView
+            };
+
+            //System.Diagnostics.Debug.WriteLine ($"{nme.Flags};{nme.X};{nme.Y};{mouseGrabView}");
+            if (MouseGrabView?.NewMouseEvent (viewRelativeMouseEvent) is true)
+            {
+                return true;
+            }
+
+            // ReSharper disable once ConditionIsAlwaysTrueOrFalse
+            if (MouseGrabView is null && deepestViewUnderMouse is Adornment)
+            {
+                // The view that grabbed the mouse has been disposed
+                return true;
+            }
+        }
+
+        return false;
+    }
 }
 }

+ 393 - 0
Terminal.Gui/App/Mouse/MouseImpl.cs

@@ -0,0 +1,393 @@
+#nullable enable
+using System.ComponentModel;
+
+namespace Terminal.Gui.App;
+
+/// <summary>
+///     INTERNAL: Implements <see cref="IMouse"/> to manage mouse event handling and state.
+///     <para>
+///         This class holds all mouse-related state that was previously in the static <see cref="Application"/> class,
+///         enabling better testability and parallel test execution.
+///     </para>
+/// </summary>
+internal class MouseImpl : IMouse
+{
+    /// <summary>
+    ///     Initializes a new instance of the <see cref="MouseImpl"/> class.
+    /// </summary>
+    public MouseImpl () { }
+
+    /// <inheritdoc/>
+    public IApplication? Application { get; set; }
+
+    /// <inheritdoc/>
+    public Point? LastMousePosition { get; set; }
+
+    /// <inheritdoc/>
+    public Point? GetLastMousePosition () { return LastMousePosition; }
+
+    /// <inheritdoc/>
+    public bool IsMouseDisabled { get; set; }
+
+    /// <inheritdoc/>
+    public List<View?> CachedViewsUnderMouse { get; } = [];
+
+    /// <inheritdoc/>
+    public event EventHandler<MouseEventArgs>? MouseEvent;
+
+    // Mouse grab functionality merged from MouseGrabHandler
+
+    /// <inheritdoc/>
+    public View? MouseGrabView { get; private set; }
+
+    /// <inheritdoc/>
+    public event EventHandler<GrabMouseEventArgs>? GrabbingMouse;
+
+    /// <inheritdoc/>
+    public event EventHandler<GrabMouseEventArgs>? UnGrabbingMouse;
+
+    /// <inheritdoc/>
+    public event EventHandler<ViewEventArgs>? GrabbedMouse;
+
+    /// <inheritdoc/>
+    public event EventHandler<ViewEventArgs>? UnGrabbedMouse;
+
+    /// <inheritdoc/>
+    public void RaiseMouseEvent (MouseEventArgs mouseEvent)
+    {
+        if (Application?.Initialized is true)
+        {
+            // LastMousePosition is only set if the application is initialized.
+            LastMousePosition = mouseEvent.ScreenPosition;
+        }
+
+        if (IsMouseDisabled)
+        {
+            return;
+        }
+
+        // The position of the mouse is the same as the screen position at the application level.
+        //Debug.Assert (mouseEvent.Position == mouseEvent.ScreenPosition);
+        mouseEvent.Position = mouseEvent.ScreenPosition;
+
+        List<View?> currentViewsUnderMouse = View.GetViewsUnderLocation (mouseEvent.ScreenPosition, ViewportSettingsFlags.TransparentMouse);
+
+        View? deepestViewUnderMouse = currentViewsUnderMouse.LastOrDefault ();
+
+        if (deepestViewUnderMouse is { })
+        {
+#if DEBUG_IDISPOSABLE
+            if (View.EnableDebugIDisposableAsserts && deepestViewUnderMouse.WasDisposed)
+            {
+                throw new ObjectDisposedException (deepestViewUnderMouse.GetType ().FullName);
+            }
+#endif
+            mouseEvent.View = deepestViewUnderMouse;
+        }
+
+        MouseEvent?.Invoke (null, mouseEvent);
+
+        if (mouseEvent.Handled)
+        {
+            return;
+        }
+
+        // Dismiss the Popover if the user presses mouse outside of it
+        if (mouseEvent.IsPressed
+            && Application?.Popover?.GetActivePopover () as View is { Visible: true } visiblePopover
+            && View.IsInHierarchy (visiblePopover, deepestViewUnderMouse, includeAdornments: true) is false)
+        {
+            ApplicationPopover.HideWithQuitCommand (visiblePopover);
+
+            // Recurse once so the event can be handled below the popover
+            RaiseMouseEvent (mouseEvent);
+
+            return;
+        }
+
+        if (HandleMouseGrab (deepestViewUnderMouse, mouseEvent))
+        {
+            return;
+        }
+
+        // May be null before the prior condition or the condition may set it as null.
+        // So, the checking must be outside the prior condition.
+        if (deepestViewUnderMouse is null)
+        {
+            return;
+        }
+
+        // if the mouse is outside the Application.Top or Application.Popover hierarchy, we don't want to
+        // send the mouse event to the deepest view under the mouse.
+        if (!View.IsInHierarchy (Application?.Top, deepestViewUnderMouse, true) && !View.IsInHierarchy (Application?.Popover?.GetActivePopover () as View, deepestViewUnderMouse, true))
+        {
+            return;
+        }
+
+        // Create a view-relative mouse event to send to the view that is under the mouse.
+        MouseEventArgs viewMouseEvent;
+
+        if (deepestViewUnderMouse is Adornment adornment)
+        {
+            Point frameLoc = adornment.ScreenToFrame (mouseEvent.ScreenPosition);
+
+            viewMouseEvent = new ()
+            {
+                Position = frameLoc,
+                Flags = mouseEvent.Flags,
+                ScreenPosition = mouseEvent.ScreenPosition,
+                View = deepestViewUnderMouse
+            };
+        }
+        else if (deepestViewUnderMouse.ViewportToScreen (Rectangle.Empty with { Size = deepestViewUnderMouse.Viewport.Size }).Contains (mouseEvent.ScreenPosition))
+        {
+            Point viewportLocation = deepestViewUnderMouse.ScreenToViewport (mouseEvent.ScreenPosition);
+
+            viewMouseEvent = new ()
+            {
+                Position = viewportLocation,
+                Flags = mouseEvent.Flags,
+                ScreenPosition = mouseEvent.ScreenPosition,
+                View = deepestViewUnderMouse
+            };
+        }
+        else
+        {
+            // The mouse was outside any View's Viewport.
+            // Debug.Fail ("This should never happen. If it does please file an Issue!!");
+
+            return;
+        }
+
+        RaiseMouseEnterLeaveEvents (viewMouseEvent.ScreenPosition, currentViewsUnderMouse);
+
+        while (deepestViewUnderMouse.NewMouseEvent (viewMouseEvent) is not true && MouseGrabView is not { })
+        {
+            if (deepestViewUnderMouse is Adornment adornmentView)
+            {
+                deepestViewUnderMouse = adornmentView.Parent?.SuperView;
+            }
+            else
+            {
+                deepestViewUnderMouse = deepestViewUnderMouse.SuperView;
+            }
+
+            if (deepestViewUnderMouse is null)
+            {
+                break;
+            }
+
+            Point boundsPoint = deepestViewUnderMouse.ScreenToViewport (mouseEvent.ScreenPosition);
+
+            viewMouseEvent = new ()
+            {
+                Position = boundsPoint,
+                Flags = mouseEvent.Flags,
+                ScreenPosition = mouseEvent.ScreenPosition,
+                View = deepestViewUnderMouse
+            };
+        }
+    }
+
+    /// <inheritdoc/>
+    public void RaiseMouseEnterLeaveEvents (Point screenPosition, List<View?> currentViewsUnderMouse)
+    {
+        // Tell any views that are no longer under the mouse that the mouse has left
+        List<View?> viewsToLeave = CachedViewsUnderMouse.Where (v => v is { } && !currentViewsUnderMouse.Contains (v)).ToList ();
+
+        foreach (View? view in viewsToLeave)
+        {
+            if (view is null)
+            {
+                continue;
+            }
+
+            view.NewMouseLeaveEvent ();
+            CachedViewsUnderMouse.Remove (view);
+        }
+
+        // Tell any views that are now under the mouse that the mouse has entered and add them to the list
+        foreach (View? view in currentViewsUnderMouse)
+        {
+            if (view is null)
+            {
+                continue;
+            }
+
+            if (CachedViewsUnderMouse.Contains (view))
+            {
+                continue;
+            }
+
+            CachedViewsUnderMouse.Add (view);
+            var raise = false;
+
+            if (view is Adornment { Parent: { } } adornmentView)
+            {
+                Point superViewLoc = adornmentView.Parent.SuperView?.ScreenToViewport (screenPosition) ?? screenPosition;
+                raise = adornmentView.Contains (superViewLoc);
+            }
+            else
+            {
+                Point superViewLoc = view.SuperView?.ScreenToViewport (screenPosition) ?? screenPosition;
+                raise = view.Contains (superViewLoc);
+            }
+
+            if (!raise)
+            {
+                continue;
+            }
+
+            CancelEventArgs eventArgs = new System.ComponentModel.CancelEventArgs ();
+            bool? cancelled = view.NewMouseEnterEvent (eventArgs);
+
+            if (cancelled is true || eventArgs.Cancel)
+            {
+                break;
+            }
+        }
+    }
+
+    /// <inheritdoc/>
+    public void ResetState ()
+    {
+        // Do not clear LastMousePosition; Popover's require it to stay set with last mouse pos.
+        CachedViewsUnderMouse.Clear ();
+        MouseEvent = null;
+    }
+
+    // Mouse grab functionality merged from MouseGrabHandler
+
+    /// <inheritdoc/>
+    public void GrabMouse (View? view)
+    {
+        if (view is null || RaiseGrabbingMouseEvent (view))
+        {
+            return;
+        }
+
+        RaiseGrabbedMouseEvent (view);
+
+        // MouseGrabView is only set if the application is initialized.
+        MouseGrabView = view;
+    }
+
+    /// <inheritdoc/>
+    public void UngrabMouse ()
+    {
+        if (MouseGrabView is null)
+        {
+            return;
+        }
+
+#if DEBUG_IDISPOSABLE
+        if (View.EnableDebugIDisposableAsserts)
+        {
+            ObjectDisposedException.ThrowIf (MouseGrabView.WasDisposed, MouseGrabView);
+        }
+#endif
+
+        if (!RaiseUnGrabbingMouseEvent (MouseGrabView))
+        {
+            View view = MouseGrabView;
+            MouseGrabView = null;
+            RaiseUnGrabbedMouseEvent (view);
+        }
+    }
+
+    /// <exception cref="Exception">A delegate callback throws an exception.</exception>
+    private bool RaiseGrabbingMouseEvent (View? view)
+    {
+        if (view is null)
+        {
+            return false;
+        }
+
+        GrabMouseEventArgs evArgs = new (view);
+        GrabbingMouse?.Invoke (view, evArgs);
+
+        return evArgs.Cancel;
+    }
+
+    /// <exception cref="Exception">A delegate callback throws an exception.</exception>
+    private bool RaiseUnGrabbingMouseEvent (View? view)
+    {
+        if (view is null)
+        {
+            return false;
+        }
+
+        GrabMouseEventArgs evArgs = new (view);
+        UnGrabbingMouse?.Invoke (view, evArgs);
+
+        return evArgs.Cancel;
+    }
+
+    /// <exception cref="Exception">A delegate callback throws an exception.</exception>
+    private void RaiseGrabbedMouseEvent (View? view)
+    {
+        if (view is null)
+        {
+            return;
+        }
+
+        GrabbedMouse?.Invoke (view, new (view));
+    }
+
+    /// <exception cref="Exception">A delegate callback throws an exception.</exception>
+    private void RaiseUnGrabbedMouseEvent (View? view)
+    {
+        if (view is null)
+        {
+            return;
+        }
+
+        UnGrabbedMouse?.Invoke (view, new (view));
+    }
+
+    /// <summary>
+    ///     Handles mouse grab logic for a mouse event.
+    /// </summary>
+    /// <param name="deepestViewUnderMouse">The deepest view under the mouse.</param>
+    /// <param name="mouseEvent">The mouse event to handle.</param>
+    /// <returns><see langword="true"/> if the event was handled by the grab handler; otherwise <see langword="false"/>.</returns>
+    public bool HandleMouseGrab (View? deepestViewUnderMouse, MouseEventArgs mouseEvent)
+    {
+        if (MouseGrabView is { })
+        {
+#if DEBUG_IDISPOSABLE
+            if (View.EnableDebugIDisposableAsserts && MouseGrabView.WasDisposed)
+            {
+                throw new ObjectDisposedException (MouseGrabView.GetType ().FullName);
+            }
+#endif
+
+            // If the mouse is grabbed, send the event to the view that grabbed it.
+            // The coordinates are relative to the Bounds of the view that grabbed the mouse.
+            Point frameLoc = MouseGrabView.ScreenToViewport (mouseEvent.ScreenPosition);
+
+            MouseEventArgs viewRelativeMouseEvent = new ()
+            {
+                Position = frameLoc,
+                Flags = mouseEvent.Flags,
+                ScreenPosition = mouseEvent.ScreenPosition,
+                View = deepestViewUnderMouse ?? MouseGrabView
+            };
+
+            //System.Diagnostics.Debug.WriteLine ($"{nme.Flags};{nme.X};{nme.Y};{mouseGrabView}");
+            if (MouseGrabView?.NewMouseEvent (viewRelativeMouseEvent) is true)
+            {
+                return true;
+            }
+
+            // ReSharper disable once ConditionIsAlwaysTrueOrFalse
+            if (MouseGrabView is null && deepestViewUnderMouse is Adornment)
+            {
+                // The view that grabbed the mouse has been disposed
+                return true;
+            }
+        }
+
+        return false;
+    }
+}

+ 5 - 0
Terminal.Gui/Drivers/DotNetDriver/NetInput.cs

@@ -86,6 +86,11 @@ public class NetInput : ConsoleInput<ConsoleKeyInfo>, INetInput
     {
     {
         base.Dispose ();
         base.Dispose ();
 
 
+        if (ConsoleDriver.RunningUnitTests)
+        {
+            return;
+        }
+
         // Disable mouse events first
         // Disable mouse events first
         Console.Out.Write (EscSeqUtils.CSI_DisableMouseEvents);
         Console.Out.Write (EscSeqUtils.CSI_DisableMouseEvents);
 
 

+ 1 - 3
Terminal.Gui/Drivers/IConsoleDriver.cs

@@ -4,9 +4,7 @@ namespace Terminal.Gui.Drivers;
 
 
 /// <summary>Base interface for Terminal.Gui ConsoleDriver implementations.</summary>
 /// <summary>Base interface for Terminal.Gui ConsoleDriver implementations.</summary>
 /// <remarks>
 /// <remarks>
-///     There are currently four implementations: - <see cref="UnixDriver"/> (for Unix and Mac) -
-///     <see cref="WindowsDriver"/> - <see cref="DotNetDriver"/> that uses the .NET Console API - <see cref="FakeConsole"/>
-///     for unit testing.
+///     There are currently four implementations: UnixDriver, WindowsDriver, DotNetDriver, and FakeDriver
 /// </remarks>
 /// </remarks>
 public interface IConsoleDriver
 public interface IConsoleDriver
 {
 {

+ 34 - 68
Terminal.Gui/README.md

@@ -8,43 +8,7 @@
 
 
 ## Project Folder Structure
 ## Project Folder Structure
 
 
-- `\` - The root folder contains the core solution and project files for the library.
-	- `Terminal.Gui.sln` - The Visual Studio solution file for the project.
-	- `Terminal.Gui.csproj` - The project file defining build configurations and dependencies.
-	- `App\` - Contains the core `Application` logic, including `Application.cs`, a `static` class that serves as the base 'application engine', managing `RunState` and `MainLoop`.
-
-- `Configuration\` - Classes related to the `ConfigurationManager` for handling application settings.
-
-- `Drivers\` - Contains the console driver implementations:
-	- `IConsoleDriver.cs` - Defines the Console Driver API.
-	- Driver implementations for .NET (`DotNetDriver`), Unix & macOS (`UnixDriver`), and Windows (`WindowsDriver`).
-
-- `Drawing\` - Classes related to rendering graphical elements in the console.
-
-- `FileServices\` - Utility classes for file operations and services.
-
-- `Input\` - Classes handling keyboard and mouse input:
-	- `Events.cs` - Defines structs and classes for keyboard and mouse events.
-
-- `Resources\` - Assets and resources used by the library.
-
-- `Text\` - Classes for text processing and formatting.
-
-- `View\` - Core `View` class hierarchy (excluding specific sub-classes):
-	- `View.cs` - The base class for non-modal visual elements such as controls.
-	- Related subdirectories for layout and positioning logic.
-
-- `ViewBase\` - Base classes and utilities for views.
-
-- `Views\` - Specific sub-classes of `View`:
-	- `Toplevel` - Base class for modal visual elements like top-level windows and dialogs, supporting `MenuBar` and `StatusBar`.
-	- `Window` - Implements framed top-level views with titles.
-	- `Dialog` - Specialized windows for user interaction.
-	- Other specialized view classes.
-
-## Showcase
-
-See the [Showcase](docs/showcase.md) to find independent applications and examples built with Terminal.Gui.
+This directory contains the core **Terminal.Gui** library source code. For a detailed repository structure, see [CONTRIBUTING.md - Repository Structure](../CONTRIBUTING.md#repository-structure).
 
 
 ## Getting Started
 ## Getting Started
 
 
@@ -52,14 +16,9 @@ For instructions on how to start using **Terminal.Gui**, refer to the [Getting S
 
 
 ## Documentation
 ## Documentation
 
 
-Comprehensive documentation for **Terminal.Gui** is available at [gui-cs.github.io/Terminal.Gui](https://gui-cs.github.io/Terminal.Gui). Key resources include:
-- [Events Deep Dive](https://gui-cs.github.io/Terminal.Gui/docs/events.html) - Detailed guide on event handling and the Cancellable Work Pattern.
-- [View Documentation](https://gui-cs.github.io/Terminal.Gui/docs/View.html) - Information on creating and customizing views.
-- [Keyboard Handling](https://gui-cs.github.io/Terminal.Gui/docs/keyboard.html) - Guide to managing keyboard input.
-- [Mouse Support](https://gui-cs.github.io/Terminal.Gui/docs/mouse.html) - Details on implementing mouse interactions.
-- [Showcase](https://gui-cs.github.io/Terminal.Gui/docs/showcase.html) - A collection of applications and examples built with Terminal.Gui.
+Comprehensive documentation is available at [gui-cs.github.io/Terminal.Gui](https://gui-cs.github.io/Terminal.Gui).
 
 
-For information on generating and updating the API documentation locally, refer to the [DocFX README](../docfx/README.md) in the `docfx` folder.
+For information on generating and updating the API documentation locally, refer to the [DocFX README](../docfx/README.md).
 
 
 ## Versioning
 ## Versioning
 
 
@@ -75,7 +34,7 @@ The project version (used in the NuGet package and `Terminal.Gui.dll`) is determ
 To define a new version, tag a commit using `git tag`:
 To define a new version, tag a commit using `git tag`:
 
 
 ```powershell
 ```powershell
-git tag v1.3.4-beta.5 -a -m "Release v1.3.4 Beta 5"
+git tag v2.1.0-beta.1 -a -m "Release v2.1.0 Beta 1"
 dotnet-gitversion /updateprojectfiles
 dotnet-gitversion /updateprojectfiles
 dotnet build -c Release
 dotnet build -c Release
 ```
 ```
@@ -92,15 +51,15 @@ To release a new version, follow these steps based on [Semantic Versioning](http
 
 
 ### Steps for Release:
 ### Steps for Release:
 
 
-1. **Verify the `develop` branch is ready for release**:
-	- Ensure all changes are committed and pushed to the `develop` branch.
-	- Ensure your local `develop` branch is up-to-date with `upstream/develop`.
+1. **Verify the `v2_develop` branch is ready for release**:
+	- Ensure all changes are committed and pushed to the `v2_develop` branch.
+	- Ensure your local `v2_develop` branch is up-to-date with `upstream/v2_develop`.
 
 
-2. **Create a pull request for the release in the `develop` branch**:
+2. **Create a pull request for the release in the `v2_develop` branch**:
 	- Title the PR as "Release vX.Y.Z".
 	- Title the PR as "Release vX.Y.Z".
 	```powershell
 	```powershell
-	git checkout develop
-	git pull upstream develop
+	git checkout v2_develop
+	git pull upstream v2_develop
 	git checkout -b vX_Y_Z
 	git checkout -b vX_Y_Z
 	git add .
 	git add .
 	git commit -m "Release vX.Y.Z"
 	git commit -m "Release vX.Y.Z"
@@ -110,28 +69,28 @@ To release a new version, follow these steps based on [Semantic Versioning](http
 
 
 3. **On github.com, verify the build action worked on your fork, then merge the PR**.
 3. **On github.com, verify the build action worked on your fork, then merge the PR**.
 
 
-4. **Pull the merged `develop` from `upstream`**:
+4. **Pull the merged `v2_develop` from `upstream`**:
 	```powershell
 	```powershell
-	git checkout develop
-	git pull upstream develop
+	git checkout v2_develop
+	git pull upstream v2_develop
 	```
 	```
 
 
-5. **Merge `develop` into `main`**:
+5. **Merge `v2_develop` into `v2_release`**:
 	```powershell
 	```powershell
-	git checkout main
-	git pull upstream main
-	git merge develop
+	git checkout v2_release
+	git pull upstream v2_release
+	git merge v2_develop
 	```
 	```
 	- Fix any merge errors.
 	- Fix any merge errors.
 
 
-6. **Create a new annotated tag for the release on `main`**:
+6. **Create a new annotated tag for the release on `v2_release`**:
 	```powershell
 	```powershell
 	git tag vX.Y.Z -a -m "Release vX.Y.Z"
 	git tag vX.Y.Z -a -m "Release vX.Y.Z"
 	```
 	```
 
 
-7. **Push the new tag to `main` on `upstream`**:
+7. **Push the new tag to `v2_release` on `upstream`**:
 	```powershell
 	```powershell
-	git push --atomic upstream main vX.Y.Z
+	git push --atomic upstream v2_release vX.Y.Z
 	```
 	```
 
 
 8. **Monitor Github Actions to ensure the NuGet publishing worked**:
 8. **Monitor Github Actions to ensure the NuGet publishing worked**:
@@ -143,18 +102,25 @@ To release a new version, follow these steps based on [Semantic Versioning](http
 10. **Add a new Release in Github**:
 10. **Add a new Release in Github**:
 	- Go to [GitHub Releases](https://github.com/gui-cs/Terminal.Gui/releases) and generate release notes with the list of PRs since the last release.
 	- Go to [GitHub Releases](https://github.com/gui-cs/Terminal.Gui/releases) and generate release notes with the list of PRs since the last release.
 
 
-11. **Update the `develop` branch with the new version**:
+11. **Update the `v2_develop` branch with the new version**:
 	```powershell
 	```powershell
-	git checkout develop
-	git pull upstream develop
-	git merge main
-	git push upstream develop
+	git checkout v2_develop
+	git pull upstream v2_develop
+	git merge v2_release
+	git push upstream v2_develop
 	```
 	```
 
 
 ## NuGet
 ## NuGet
 
 
-The official NuGet package for Terminal.Gui is available at [https://www.nuget.org/packages/Terminal.Gui](https://www.nuget.org/packages/Terminal.Gui). When a new version tag is defined and merged into `main`, a NuGet package is automatically generated by a GitHub Action. Pre-release versions (e.g., `1.3.4-beta.5`) are tagged as pre-release on NuGet.
+The official NuGet package for Terminal.Gui is available at [https://www.nuget.org/packages/Terminal.Gui](https://www.nuget.org/packages/Terminal.Gui). When a new version tag is defined and merged into `v2_release`, a NuGet package is automatically generated by a GitHub Action. Pre-release versions (e.g., `2.0.0-beta.5`) are tagged as pre-release on NuGet.
 
 
 ## Contributing
 ## Contributing
 
 
-We welcome contributions from the community. For detailed guidelines on how to contribute, including coding style, unit tests, and pull request processes, please refer to [CONTRIBUTING.md](https://github.com/gui-cs/Terminal.Gui/blob/master/CONTRIBUTING.md).
+We welcome contributions from the community. For complete contribution guidelines, including:
+- Build and test instructions
+- Coding conventions and style rules
+- Testing requirements and patterns
+- Pull request guidelines
+- CI/CD workflows
+
+Please refer to [CONTRIBUTING.md](../CONTRIBUTING.md) in the repository root.

+ 9 - 9
Terminal.Gui/ViewBase/Adornment/Border.Arrangment.cs

@@ -431,9 +431,9 @@ public partial class Border
 
 
         Application.MouseEvent -= ApplicationOnMouseEvent;
         Application.MouseEvent -= ApplicationOnMouseEvent;
 
 
-        if (Application.MouseGrabHandler.MouseGrabView == this && _dragPosition.HasValue)
+        if (Application.Mouse.MouseGrabView == this && _dragPosition.HasValue)
         {
         {
-            Application.MouseGrabHandler.UngrabMouse ();
+            Application.Mouse.UngrabMouse ();
         }
         }
 
 
         // Clean up all arrangement buttons
         // Clean up all arrangement buttons
@@ -498,7 +498,7 @@ public partial class Border
                 // Set the start grab point to the Frame coords
                 // Set the start grab point to the Frame coords
                 _startGrabPoint = new (mouseEvent.Position.X + Frame.X, mouseEvent.Position.Y + Frame.Y);
                 _startGrabPoint = new (mouseEvent.Position.X + Frame.X, mouseEvent.Position.Y + Frame.Y);
                 _dragPosition = mouseEvent.Position;
                 _dragPosition = mouseEvent.Position;
-                Application.MouseGrabHandler.GrabMouse (this);
+                Application.Mouse.GrabMouse (this);
 
 
                 // Determine the mode based on where the click occurred
                 // Determine the mode based on where the click occurred
                 ViewArrangement arrangeMode = DetermineArrangeModeFromClick ();
                 ViewArrangement arrangeMode = DetermineArrangeModeFromClick ();
@@ -511,7 +511,7 @@ public partial class Border
             return true;
             return true;
         }
         }
 
 
-        if (mouseEvent.Flags is (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition) && Application.MouseGrabHandler.MouseGrabView == this)
+        if (mouseEvent.Flags is (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition) && Application.Mouse.MouseGrabView == this)
         {
         {
             if (_dragPosition.HasValue)
             if (_dragPosition.HasValue)
             {
             {
@@ -523,7 +523,7 @@ public partial class Border
         if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Released) && _dragPosition.HasValue)
         if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Released) && _dragPosition.HasValue)
         {
         {
             _dragPosition = null;
             _dragPosition = null;
-            Application.MouseGrabHandler.UngrabMouse ();
+            Application.Mouse.UngrabMouse ();
 
 
             EndArrangeMode ();
             EndArrangeMode ();
 
 
@@ -763,7 +763,7 @@ public partial class Border
 
 
     private void Application_GrabbingMouse (object? sender, GrabMouseEventArgs e)
     private void Application_GrabbingMouse (object? sender, GrabMouseEventArgs e)
     {
     {
-        if (Application.MouseGrabHandler.MouseGrabView == this && _dragPosition.HasValue)
+        if (Application.Mouse.MouseGrabView == this && _dragPosition.HasValue)
         {
         {
             e.Cancel = true;
             e.Cancel = true;
         }
         }
@@ -771,7 +771,7 @@ public partial class Border
 
 
     private void Application_UnGrabbingMouse (object? sender, GrabMouseEventArgs e)
     private void Application_UnGrabbingMouse (object? sender, GrabMouseEventArgs e)
     {
     {
-        if (Application.MouseGrabHandler.MouseGrabView == this && _dragPosition.HasValue)
+        if (Application.Mouse.MouseGrabView == this && _dragPosition.HasValue)
         {
         {
             e.Cancel = true;
             e.Cancel = true;
         }
         }
@@ -784,8 +784,8 @@ public partial class Border
     /// <inheritdoc/>
     /// <inheritdoc/>
     protected override void Dispose (bool disposing)
     protected override void Dispose (bool disposing)
     {
     {
-        Application.MouseGrabHandler.GrabbingMouse -= Application_GrabbingMouse;
-        Application.MouseGrabHandler.UnGrabbingMouse -= Application_UnGrabbingMouse;
+        Application.Mouse.GrabbingMouse -= Application_GrabbingMouse;
+        Application.Mouse.UnGrabbingMouse -= Application_UnGrabbingMouse;
 
 
         _dragPosition = null;
         _dragPosition = null;
         base.Dispose (disposing);
         base.Dispose (disposing);

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

@@ -50,8 +50,8 @@ public partial class Border : Adornment
         CanFocus = false;
         CanFocus = false;
         TabStop = TabBehavior.TabGroup;
         TabStop = TabBehavior.TabGroup;
 
 
-        Application.MouseGrabHandler.GrabbingMouse += Application_GrabbingMouse;
-        Application.MouseGrabHandler.UnGrabbingMouse += Application_UnGrabbingMouse;
+        Application.Mouse.GrabbingMouse += Application_GrabbingMouse;
+        Application.Mouse.UnGrabbingMouse += Application_UnGrabbingMouse;
 
 
         ThicknessChanged += OnThicknessChanged;
         ThicknessChanged += OnThicknessChanged;
     }
     }

+ 21 - 10
Terminal.Gui/ViewBase/View.Drawing.cs

@@ -377,6 +377,16 @@ public partial class View // Drawing APIs
 
 
     private void DoDrawText (DrawContext? context = null)
     private void DoDrawText (DrawContext? context = null)
     {
     {
+        if (!NeedsDraw)
+        {
+            return;
+        }
+
+        if (!string.IsNullOrEmpty (TextFormatter.Text))
+        {
+            TextFormatter.NeedsFormat = true;
+        }
+
         if (OnDrawingText (context))
         if (OnDrawingText (context))
         {
         {
             return;
             return;
@@ -397,6 +407,9 @@ public partial class View // Drawing APIs
         }
         }
 
 
         DrawText (context);
         DrawText (context);
+
+        OnDrewText();
+        DrewText?.Invoke(this, EventArgs.Empty);
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -425,11 +438,6 @@ public partial class View // Drawing APIs
     /// <param name="context">The draw context to report drawn areas to.</param>
     /// <param name="context">The draw context to report drawn areas to.</param>
     public void DrawText (DrawContext? context = null)
     public void DrawText (DrawContext? context = null)
     {
     {
-        if (!string.IsNullOrEmpty (TextFormatter.Text))
-        {
-            TextFormatter.NeedsFormat = true;
-        }
-
         var drawRect = new Rectangle (ContentToScreen (Point.Empty), GetContentSize ());
         var drawRect = new Rectangle (ContentToScreen (Point.Empty), GetContentSize ());
 
 
         // Use GetDrawRegion to get precise drawn areas
         // Use GetDrawRegion to get precise drawn areas
@@ -438,11 +446,6 @@ public partial class View // Drawing APIs
         // Report the drawn area to the context
         // Report the drawn area to the context
         context?.AddDrawnRegion (textRegion);
         context?.AddDrawnRegion (textRegion);
 
 
-        if (!NeedsDraw)
-        {
-            return;
-        }
-
         TextFormatter?.Draw (
         TextFormatter?.Draw (
                              drawRect,
                              drawRect,
                              HasFocus ? GetAttributeForRole (VisualRole.Focus) : GetAttributeForRole (VisualRole.Normal),
                              HasFocus ? GetAttributeForRole (VisualRole.Focus) : GetAttributeForRole (VisualRole.Normal),
@@ -454,6 +457,14 @@ public partial class View // Drawing APIs
         SetSubViewNeedsDraw ();
         SetSubViewNeedsDraw ();
     }
     }
 
 
+    /// <summary>
+    ///     Called when the <see cref="Text"/> of the View has been drawn.
+    /// </summary>
+    protected virtual void OnDrewText () { }
+
+    /// <summary>Raised when the <see cref="Text"/> of the View has been drawn.</summary>
+    public event EventHandler? DrewText;
+
     #endregion DrawText
     #endregion DrawText
     #region DrawContent
     #region DrawContent
 
 

+ 6 - 6
Terminal.Gui/ViewBase/View.Mouse.cs

@@ -16,7 +16,7 @@ public partial class View // Mouse APIs
 
 
     private void SetupMouse ()
     private void SetupMouse ()
     {
     {
-        MouseHeldDown = new MouseHeldDown (this, Application.TimedEvents,Application.MouseGrabHandler);
+        MouseHeldDown = new MouseHeldDown (this, Application.TimedEvents,Application.Mouse);
         MouseBindings = new ();
         MouseBindings = new ();
 
 
         // TODO: Should the default really work with any button or just button1?
         // TODO: Should the default really work with any button or just button1?
@@ -375,7 +375,7 @@ public partial class View // Mouse APIs
 
 
         if (mouseEvent.IsReleased)
         if (mouseEvent.IsReleased)
         {
         {
-            if (Application.MouseGrabHandler.MouseGrabView == this)
+            if (Application.Mouse.MouseGrabView == this)
             {
             {
                 //Logging.Debug ($"{Id} - {MouseState}");
                 //Logging.Debug ($"{Id} - {MouseState}");
                 MouseState &= ~MouseState.Pressed;
                 MouseState &= ~MouseState.Pressed;
@@ -407,9 +407,9 @@ public partial class View // Mouse APIs
         if (mouseEvent.IsPressed)
         if (mouseEvent.IsPressed)
         {
         {
             // The first time we get pressed event, grab the mouse and set focus
             // The first time we get pressed event, grab the mouse and set focus
-            if (Application.MouseGrabHandler.MouseGrabView != this)
+            if (Application.Mouse.MouseGrabView != this)
             {
             {
-                Application.MouseGrabHandler.GrabMouse (this);
+                Application.Mouse.GrabMouse (this);
 
 
                 if (!HasFocus && CanFocus)
                 if (!HasFocus && CanFocus)
                 {
                 {
@@ -541,10 +541,10 @@ public partial class View // Mouse APIs
     {
     {
         mouseEvent.Handled = false;
         mouseEvent.Handled = false;
 
 
-        if (Application.MouseGrabHandler.MouseGrabView == this && mouseEvent.IsSingleClicked)
+        if (Application.Mouse.MouseGrabView == this && mouseEvent.IsSingleClicked)
         {
         {
             // We're grabbed. Clicked event comes after the last Release. This is our signal to ungrab
             // We're grabbed. Clicked event comes after the last Release. This is our signal to ungrab
-            Application.MouseGrabHandler.UngrabMouse ();
+            Application.Mouse.UngrabMouse ();
 
 
             // TODO: Prove we need to unset MouseState.Pressed and MouseState.PressedOutside here
             // TODO: Prove we need to unset MouseState.Pressed and MouseState.PressedOutside here
             // TODO: There may be perf gains if we don't unset these flags here
             // TODO: There may be perf gains if we don't unset these flags here

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

@@ -72,9 +72,9 @@ public partial class View : IDisposable, ISupportInitializeNotification
             DisposeAdornments ();
             DisposeAdornments ();
             DisposeScrollBars ();
             DisposeScrollBars ();
 
 
-            if (Application.MouseGrabHandler.MouseGrabView == this)
+            if (Application.Mouse.MouseGrabView == this)
             {
             {
-                Application.MouseGrabHandler.UngrabMouse ();
+                Application.Mouse.UngrabMouse ();
             }
             }
 
 
             for (int i = InternalSubViews.Count - 1; i >= 0; i--)
             for (int i = InternalSubViews.Count - 1; i >= 0; i--)

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

@@ -125,7 +125,7 @@ public abstract partial class PopupAutocomplete : AutocompleteBase
             {
             {
                 Visible = true;
                 Visible = true;
                 HostControl?.SetNeedsDraw ();
                 HostControl?.SetNeedsDraw ();
-                Application.MouseGrabHandler.UngrabMouse ();
+                Application.Mouse.UngrabMouse ();
 
 
                 return false;
                 return false;
             }
             }

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

@@ -958,7 +958,7 @@ public class ComboBox : View, IDesignable
                 {
                 {
                     _isFocusing = true;
                     _isFocusing = true;
                     _highlighted = _container.SelectedItem;
                     _highlighted = _container.SelectedItem;
-                    Application.MouseGrabHandler.GrabMouse (this);
+                    Application.Mouse.GrabMouse (this);
                 }
                 }
             }
             }
             else
             else
@@ -967,7 +967,7 @@ public class ComboBox : View, IDesignable
                 {
                 {
                     _isFocusing = false;
                     _isFocusing = false;
                     _highlighted = _container.SelectedItem;
                     _highlighted = _container.SelectedItem;
-                    Application.MouseGrabHandler.UngrabMouse ();
+                    Application.Mouse.UngrabMouse ();
                 }
                 }
             }
             }
         }
         }

+ 30 - 0
Terminal.Gui/Views/Dialog.cs

@@ -138,4 +138,34 @@ public class Dialog : Window
     /// </summary>
     /// </summary>
     [ConfigurationProperty (Scope = typeof (ThemeScope))]
     [ConfigurationProperty (Scope = typeof (ThemeScope))]
     public new static ShadowStyle DefaultShadow { get; set; } = ShadowStyle.Transparent;
     public new static ShadowStyle DefaultShadow { get; set; } = ShadowStyle.Transparent;
+
+
+    // Dialogs are Modal and Focus is indicated by their Border. The following code ensures the
+    // Text of the dialog (e.g. for a MessageBox) is always drawn using the Normal Attribute.
+    private bool _drawingText;
+
+    /// <inheritdoc/>
+    protected override bool OnDrawingText ()
+    {
+        _drawingText = true;
+        return false;
+    }
+
+    /// <inheritdoc/>
+    protected override void OnDrewText ()
+    {
+        _drawingText = false;
+    }
+
+    /// <inheritdoc />
+    protected override bool OnGettingAttributeForRole (in VisualRole role, ref Attribute currentAttribute)
+    {
+        if (_drawingText && role is VisualRole.Focus && Border?.Thickness != Thickness.Empty)
+        {
+            currentAttribute = GetScheme ().Normal;
+            return true;
+        }
+
+        return false;
+    }
 }
 }

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

@@ -19,7 +19,7 @@ internal sealed class Menu : View
         }
         }
 
 
         Application.MouseEvent += Application_RootMouseEvent;
         Application.MouseEvent += Application_RootMouseEvent;
-        Application.MouseGrabHandler.UnGrabbedMouse += Application_UnGrabbedMouse;
+        Application.Mouse.UnGrabbedMouse += Application_UnGrabbedMouse;
 
 
         // Things this view knows how to do
         // Things this view knows how to do
         AddCommand (Command.Up, () => MoveUp ());
         AddCommand (Command.Up, () => MoveUp ());
@@ -220,7 +220,7 @@ internal sealed class Menu : View
             return;
             return;
         }
         }
 
 
-        Application.MouseGrabHandler.UngrabMouse ();
+        Application.Mouse.UngrabMouse ();
         _host.CloseAllMenus ();
         _host.CloseAllMenus ();
         Application.LayoutAndDraw (true);
         Application.LayoutAndDraw (true);
 
 
@@ -238,7 +238,7 @@ internal sealed class Menu : View
         }
         }
 
 
         Application.MouseEvent -= Application_RootMouseEvent;
         Application.MouseEvent -= Application_RootMouseEvent;
-        Application.MouseGrabHandler.UnGrabbedMouse -= Application_UnGrabbedMouse;
+        Application.Mouse.UnGrabbedMouse -= Application_UnGrabbedMouse;
         base.Dispose (disposing);
         base.Dispose (disposing);
     }
     }
 
 
@@ -535,7 +535,7 @@ internal sealed class Menu : View
 
 
     private void CloseAllMenus ()
     private void CloseAllMenus ()
     {
     {
-        Application.MouseGrabHandler.UngrabMouse ();
+        Application.Mouse.UngrabMouse ();
         _host.CloseAllMenus ();
         _host.CloseAllMenus ();
     }
     }
 
 

+ 33 - 33
Terminal.Gui/Views/Menuv1/MenuBar.cs

@@ -442,12 +442,12 @@ public class MenuBar : View, IDesignable
 
 
         if (_isContextMenuLoading)
         if (_isContextMenuLoading)
         {
         {
-            Application.MouseGrabHandler.GrabMouse (_openMenu);
+            Application.Mouse.GrabMouse (_openMenu);
             _isContextMenuLoading = false;
             _isContextMenuLoading = false;
         }
         }
         else
         else
         {
         {
-            Application.MouseGrabHandler.GrabMouse (this);
+            Application.Mouse.GrabMouse (this);
         }
         }
     }
     }
 
 
@@ -524,16 +524,16 @@ public class MenuBar : View, IDesignable
 
 
         SetNeedsDraw ();
         SetNeedsDraw ();
 
 
-        if (Application.MouseGrabHandler.MouseGrabView is { } && Application.MouseGrabHandler.MouseGrabView is MenuBar && Application.MouseGrabHandler.MouseGrabView != this)
+        if (Application.Mouse.MouseGrabView is { } && Application.Mouse.MouseGrabView is MenuBar && Application.Mouse.MouseGrabView != this)
         {
         {
-            var menuBar = Application.MouseGrabHandler.MouseGrabView as MenuBar;
+            var menuBar = Application.Mouse.MouseGrabView as MenuBar;
 
 
             if (menuBar!.IsMenuOpen)
             if (menuBar!.IsMenuOpen)
             {
             {
                 menuBar.CleanUp ();
                 menuBar.CleanUp ();
             }
             }
         }
         }
-        Application.MouseGrabHandler.UngrabMouse ();
+        Application.Mouse.UngrabMouse ();
         _isCleaning = false;
         _isCleaning = false;
     }
     }
 
 
@@ -556,7 +556,7 @@ public class MenuBar : View, IDesignable
                 _selected = -1;
                 _selected = -1;
             }
             }
 
 
-            Application.MouseGrabHandler.UngrabMouse ();
+            Application.Mouse.UngrabMouse ();
         }
         }
 
 
         if (OpenCurrentMenu is { })
         if (OpenCurrentMenu is { })
@@ -622,9 +622,9 @@ public class MenuBar : View, IDesignable
                     _previousFocused.SetFocus ();
                     _previousFocused.SetFocus ();
                 }
                 }
 
 
-                if (Application.MouseGrabHandler.MouseGrabView == _openMenu)
+                if (Application.Mouse.MouseGrabView == _openMenu)
                 {
                 {
-                    Application.MouseGrabHandler.UngrabMouse ();
+                    Application.Mouse.UngrabMouse ();
                 }
                 }
                 _openMenu?.Dispose ();
                 _openMenu?.Dispose ();
                 _openMenu = null;
                 _openMenu = null;
@@ -652,9 +652,9 @@ public class MenuBar : View, IDesignable
                     if (OpenCurrentMenu is { })
                     if (OpenCurrentMenu is { })
                     {
                     {
                         SuperView?.Remove (OpenCurrentMenu);
                         SuperView?.Remove (OpenCurrentMenu);
-                        if (Application.MouseGrabHandler.MouseGrabView == OpenCurrentMenu)
+                        if (Application.Mouse.MouseGrabView == OpenCurrentMenu)
                         {
                         {
-                            Application.MouseGrabHandler.UngrabMouse ();
+                            Application.Mouse.UngrabMouse ();
                         }
                         }
                         OpenCurrentMenu.Dispose ();
                         OpenCurrentMenu.Dispose ();
                         OpenCurrentMenu = null;
                         OpenCurrentMenu = null;
@@ -845,9 +845,9 @@ public class MenuBar : View, IDesignable
                 if (_openMenu is { })
                 if (_openMenu is { })
                 {
                 {
                     SuperView?.Remove (_openMenu);
                     SuperView?.Remove (_openMenu);
-                    if (Application.MouseGrabHandler.MouseGrabView == _openMenu)
+                    if (Application.Mouse.MouseGrabView == _openMenu)
                     {
                     {
-                        Application.MouseGrabHandler.UngrabMouse ();
+                        Application.Mouse.UngrabMouse ();
                     }
                     }
                     _openMenu.Dispose ();
                     _openMenu.Dispose ();
                     _openMenu = null;
                     _openMenu = null;
@@ -935,7 +935,7 @@ public class MenuBar : View, IDesignable
                             Host = this, X = first!.Frame.Left, Y = first.Frame.Top, BarItems = newSubMenu
                             Host = this, X = first!.Frame.Left, Y = first.Frame.Top, BarItems = newSubMenu
                         };
                         };
                         last!.Visible = false;
                         last!.Visible = false;
-                        Application.MouseGrabHandler.GrabMouse (OpenCurrentMenu);
+                        Application.Mouse.GrabMouse (OpenCurrentMenu);
                     }
                     }
 
 
                     OpenCurrentMenu._previousSubFocused = last._previousSubFocused;
                     OpenCurrentMenu._previousSubFocused = last._previousSubFocused;
@@ -1029,9 +1029,9 @@ public class MenuBar : View, IDesignable
             foreach (Menu item in _openSubMenu)
             foreach (Menu item in _openSubMenu)
             {
             {
                 SuperView?.Remove (item);
                 SuperView?.Remove (item);
-                if (Application.MouseGrabHandler.MouseGrabView == item)
+                if (Application.Mouse.MouseGrabView == item)
                 {
                 {
-                    Application.MouseGrabHandler.UngrabMouse ();
+                    Application.Mouse.UngrabMouse ();
                 }
                 }
                 item.Dispose ();
                 item.Dispose ();
             }
             }
@@ -1137,7 +1137,7 @@ public class MenuBar : View, IDesignable
             return false;
             return false;
         }
         }
 
 
-        Application.MouseGrabHandler.UngrabMouse ();
+        Application.Mouse.UngrabMouse ();
         CloseAllMenus ();
         CloseAllMenus ();
         Application.LayoutAndDraw (true);
         Application.LayoutAndDraw (true);
         _openedByAltKey = true;
         _openedByAltKey = true;
@@ -1209,15 +1209,15 @@ public class MenuBar : View, IDesignable
             Point screen = ViewportToScreen (new Point (0, i));
             Point screen = ViewportToScreen (new Point (0, i));
             var menu = new Menu { Host = this, X = screen.X, Y = screen.Y, BarItems = mi };
             var menu = new Menu { Host = this, X = screen.X, Y = screen.Y, BarItems = mi };
             menu.Run (mi.Action);
             menu.Run (mi.Action);
-            if (Application.MouseGrabHandler.MouseGrabView == menu)
+            if (Application.Mouse.MouseGrabView == menu)
             {
             {
-                Application.MouseGrabHandler.UngrabMouse ();
+                Application.Mouse.UngrabMouse ();
             }
             }
             menu.Dispose ();
             menu.Dispose ();
         }
         }
         else
         else
         {
         {
-            Application.MouseGrabHandler.GrabMouse (this);
+            Application.Mouse.GrabMouse (this);
             _selected = i;
             _selected = i;
             OpenMenu (i);
             OpenMenu (i);
 
 
@@ -1280,9 +1280,9 @@ public class MenuBar : View, IDesignable
                 SuperView!.Remove (menu);
                 SuperView!.Remove (menu);
                 _openSubMenu.Remove (menu);
                 _openSubMenu.Remove (menu);
 
 
-                if (Application.MouseGrabHandler.MouseGrabView == menu)
+                if (Application.Mouse.MouseGrabView == menu)
                 {
                 {
-                    Application.MouseGrabHandler.GrabMouse (this);
+                    Application.Mouse.GrabMouse (this);
                 }
                 }
 
 
                 menu.Dispose ();
                 menu.Dispose ();
@@ -1458,9 +1458,9 @@ public class MenuBar : View, IDesignable
                             Point screen = ViewportToScreen (new Point (0, i));
                             Point screen = ViewportToScreen (new Point (0, i));
                             var menu = new Menu { Host = this, X = screen.X, Y = screen.Y, BarItems = Menus [i] };
                             var menu = new Menu { Host = this, X = screen.X, Y = screen.Y, BarItems = Menus [i] };
                             menu.Run (Menus [i].Action);
                             menu.Run (Menus [i].Action);
-                            if (Application.MouseGrabHandler.MouseGrabView == menu)
+                            if (Application.Mouse.MouseGrabView == menu)
                             {
                             {
-                                Application.MouseGrabHandler.UngrabMouse ();
+                                Application.Mouse.UngrabMouse ();
                             }
                             }
 
 
                             menu.Dispose ();
                             menu.Dispose ();
@@ -1535,7 +1535,7 @@ public class MenuBar : View, IDesignable
 
 
     internal bool HandleGrabView (MouseEventArgs me, View current)
     internal bool HandleGrabView (MouseEventArgs me, View current)
     {
     {
-        if (Application.MouseGrabHandler.MouseGrabView is { })
+        if (Application.Mouse.MouseGrabView is { })
         {
         {
             if (me.View is MenuBar or Menu)
             if (me.View is MenuBar or Menu)
             {
             {
@@ -1546,7 +1546,7 @@ public class MenuBar : View, IDesignable
                     if (me.Flags == MouseFlags.Button1Clicked)
                     if (me.Flags == MouseFlags.Button1Clicked)
                     {
                     {
                         mbar.CleanUp ();
                         mbar.CleanUp ();
-                        Application.MouseGrabHandler.GrabMouse (me.View);
+                        Application.Mouse.GrabMouse (me.View);
                     }
                     }
                     else
                     else
                     {
                     {
@@ -1556,10 +1556,10 @@ public class MenuBar : View, IDesignable
                     }
                     }
                 }
                 }
 
 
-                if (Application.MouseGrabHandler.MouseGrabView != me.View)
+                if (Application.Mouse.MouseGrabView != me.View)
                 {
                 {
                     View v = me.View;
                     View v = me.View;
-                    Application.MouseGrabHandler.GrabMouse (v);
+                    Application.Mouse.GrabMouse (v);
 
 
                     return true;
                     return true;
                 }
                 }
@@ -1567,7 +1567,7 @@ public class MenuBar : View, IDesignable
                 if (me.View != current)
                 if (me.View != current)
                 {
                 {
                     View v = me.View;
                     View v = me.View;
-                    Application.MouseGrabHandler.GrabMouse (v);
+                    Application.Mouse.GrabMouse (v);
                     MouseEventArgs nme;
                     MouseEventArgs nme;
 
 
                     if (me.Position.Y > -1)
                     if (me.Position.Y > -1)
@@ -1599,7 +1599,7 @@ public class MenuBar : View, IDesignable
                      && me.Flags != MouseFlags.ReportMousePosition
                      && me.Flags != MouseFlags.ReportMousePosition
                      && me.Flags != 0)
                      && me.Flags != 0)
             {
             {
-                Application.MouseGrabHandler.UngrabMouse ();
+                Application.Mouse.UngrabMouse ();
 
 
                 if (IsMenuOpen)
                 if (IsMenuOpen)
                 {
                 {
@@ -1625,11 +1625,11 @@ public class MenuBar : View, IDesignable
                                           MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
                                           MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
                                          )))
                                          )))
         {
         {
-            Application.MouseGrabHandler.GrabMouse (current);
+            Application.Mouse.GrabMouse (current);
         }
         }
         else if (IsMenuOpen && (me.View is MenuBar || me.View is Menu))
         else if (IsMenuOpen && (me.View is MenuBar || me.View is Menu))
         {
         {
-            Application.MouseGrabHandler.GrabMouse (me.View);
+            Application.Mouse.GrabMouse (me.View);
         }
         }
         else
         else
         {
         {
@@ -1645,7 +1645,7 @@ public class MenuBar : View, IDesignable
 
 
     private MenuBar? GetMouseGrabViewInstance (View? view)
     private MenuBar? GetMouseGrabViewInstance (View? view)
     {
     {
-        if (view is null || Application.MouseGrabHandler.MouseGrabView is null)
+        if (view is null || Application.Mouse.MouseGrabView is null)
         {
         {
             return null;
             return null;
         }
         }
@@ -1661,7 +1661,7 @@ public class MenuBar : View, IDesignable
             hostView = ((Menu)view).Host;
             hostView = ((Menu)view).Host;
         }
         }
 
 
-        View grabView = Application.MouseGrabHandler.MouseGrabView;
+        View grabView = Application.Mouse.MouseGrabView;
         MenuBar? hostGrabView = null;
         MenuBar? hostGrabView = null;
 
 
         if (grabView is MenuBar bar)
         if (grabView is MenuBar bar)

+ 4 - 4
Terminal.Gui/Views/ScrollBar/ScrollSlider.cs

@@ -307,9 +307,9 @@ public class ScrollSlider : View, IOrientation, IDesignable
         {
         {
             if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed) && _lastLocation == -1)
             if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed) && _lastLocation == -1)
             {
             {
-                if (Application.MouseGrabHandler.MouseGrabView != this)
+                if (Application.Mouse.MouseGrabView != this)
                 {
                 {
-                    Application.MouseGrabHandler.GrabMouse (this);
+                    Application.Mouse.GrabMouse (this);
                     _lastLocation = location;
                     _lastLocation = location;
                 }
                 }
             }
             }
@@ -333,9 +333,9 @@ public class ScrollSlider : View, IOrientation, IDesignable
             {
             {
                 _lastLocation = -1;
                 _lastLocation = -1;
 
 
-                if (Application.MouseGrabHandler.MouseGrabView == this)
+                if (Application.Mouse.MouseGrabView == this)
                 {
                 {
-                    Application.MouseGrabHandler.UngrabMouse ();
+                    Application.Mouse.UngrabMouse ();
                 }
                 }
             }
             }
 
 

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

@@ -1311,7 +1311,7 @@ public class Slider<T> : View, IOrientation
             {
             {
                 _dragPosition = mouseEvent.Position;
                 _dragPosition = mouseEvent.Position;
                 _moveRenderPosition = ClampMovePosition ((Point)_dragPosition);
                 _moveRenderPosition = ClampMovePosition ((Point)_dragPosition);
-                Application.MouseGrabHandler.GrabMouse (this);
+                Application.Mouse.GrabMouse (this);
             }
             }
 
 
             SetNeedsDraw ();
             SetNeedsDraw ();
@@ -1357,7 +1357,7 @@ public class Slider<T> : View, IOrientation
             || mouseEvent.Flags.HasFlag (MouseFlags.Button1Clicked))
             || mouseEvent.Flags.HasFlag (MouseFlags.Button1Clicked))
         {
         {
             // End Drag
             // End Drag
-            Application.MouseGrabHandler.UngrabMouse ();
+            Application.Mouse.UngrabMouse ();
             _dragPosition = null;
             _dragPosition = null;
             _moveRenderPosition = null;
             _moveRenderPosition = null;
 
 

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

@@ -855,16 +855,16 @@ public class TextField : View, IDesignable
             _isButtonReleased = false;
             _isButtonReleased = false;
             PrepareSelection (x);
             PrepareSelection (x);
 
 
-            if (Application.MouseGrabHandler.MouseGrabView is null)
+            if (Application.Mouse.MouseGrabView is null)
             {
             {
-                Application.MouseGrabHandler.GrabMouse (this);
+                Application.Mouse.GrabMouse (this);
             }
             }
         }
         }
         else if (ev.Flags == MouseFlags.Button1Released)
         else if (ev.Flags == MouseFlags.Button1Released)
         {
         {
             _isButtonReleased = true;
             _isButtonReleased = true;
             _isButtonPressed = false;
             _isButtonPressed = false;
-            Application.MouseGrabHandler.UngrabMouse ();
+            Application.Mouse.UngrabMouse ();
         }
         }
         else if (ev.Flags == MouseFlags.Button1DoubleClicked)
         else if (ev.Flags == MouseFlags.Button1DoubleClicked)
         {
         {
@@ -1007,12 +1007,12 @@ public class TextField : View, IDesignable
     /// <inheritdoc/>
     /// <inheritdoc/>
     protected override void OnHasFocusChanged (bool newHasFocus, View previousFocusedView, View view)
     protected override void OnHasFocusChanged (bool newHasFocus, View previousFocusedView, View view)
     {
     {
-        if (Application.MouseGrabHandler.MouseGrabView is { } && Application.MouseGrabHandler.MouseGrabView == this)
+        if (Application.Mouse.MouseGrabView is { } && Application.Mouse.MouseGrabView == this)
         {
         {
-            Application.MouseGrabHandler.UngrabMouse ();
+            Application.Mouse.UngrabMouse ();
         }
         }
 
 
-        //if (SelectedLength != 0 && !(Application.MouseGrabHandler.MouseGrabView is MenuBar))
+        //if (SelectedLength != 0 && !(Application.Mouse.MouseGrabView is MenuBar))
         //	ClearAllSelection ();
         //	ClearAllSelection ();
     }
     }
 
 

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

@@ -1676,15 +1676,15 @@ public class TextView : View, IDesignable
             _lastWasKill = false;
             _lastWasKill = false;
             _columnTrack = CurrentColumn;
             _columnTrack = CurrentColumn;
 
 
-            if (Application.MouseGrabHandler.MouseGrabView is null)
+            if (Application.Mouse.MouseGrabView is null)
             {
             {
-                Application.MouseGrabHandler.GrabMouse (this);
+                Application.Mouse.GrabMouse (this);
             }
             }
         }
         }
         else if (ev.Flags.HasFlag (MouseFlags.Button1Released))
         else if (ev.Flags.HasFlag (MouseFlags.Button1Released))
         {
         {
             _isButtonReleased = true;
             _isButtonReleased = true;
-            Application.MouseGrabHandler.UngrabMouse ();
+            Application.Mouse.UngrabMouse ();
         }
         }
         else if (ev.Flags.HasFlag (MouseFlags.Button1DoubleClicked))
         else if (ev.Flags.HasFlag (MouseFlags.Button1DoubleClicked))
         {
         {
@@ -1886,9 +1886,9 @@ public class TextView : View, IDesignable
     /// <inheritdoc/>
     /// <inheritdoc/>
     protected override void OnHasFocusChanged (bool newHasFocus, View? previousFocusedView, View? view)
     protected override void OnHasFocusChanged (bool newHasFocus, View? previousFocusedView, View? view)
     {
     {
-        if (Application.MouseGrabHandler.MouseGrabView is { } && Application.MouseGrabHandler.MouseGrabView == this)
+        if (Application.Mouse.MouseGrabView is { } && Application.Mouse.MouseGrabView == this)
         {
         {
-            Application.MouseGrabHandler.UngrabMouse ();
+            Application.Mouse.UngrabMouse ();
         }
         }
     }
     }
 
 
@@ -2032,7 +2032,7 @@ public class TextView : View, IDesignable
             return null;
             return null;
         }
         }
 
 
-        if (Application.MouseGrabHandler.MouseGrabView == this && IsSelecting)
+        if (Application.Mouse.MouseGrabView == this && IsSelecting)
         {
         {
             // BUGBUG: customized rect aren't supported now because the Redraw isn't using the Intersect method.
             // BUGBUG: customized rect aren't supported now because the Redraw isn't using the Intersect method.
             //var minRow = Math.Min (Math.Max (Math.Min (selectionStartRow, currentRow) - topRow, 0), Viewport.Height);
             //var minRow = Math.Min (Math.Max (Math.Min (selectionStartRow, currentRow) - topRow, 0), Viewport.Height);

+ 3 - 2
Terminal.sln

@@ -1,7 +1,7 @@
 
 
 Microsoft Visual Studio Solution File, Format Version 12.00
 Microsoft Visual Studio Solution File, Format Version 12.00
 # Visual Studio Version 18
 # Visual Studio Version 18
-VisualStudioVersion = 18.0.11018.127 d18.0
+VisualStudioVersion = 18.0.11018.127
 MinimumVisualStudioVersion = 10.0.40219.1
 MinimumVisualStudioVersion = 10.0.40219.1
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Terminal.Gui", "Terminal.Gui\Terminal.Gui.csproj", "{00F366F8-DEE4-482C-B9FD-6DB0200B79E5}"
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Terminal.Gui", "Terminal.Gui\Terminal.Gui.csproj", "{00F366F8-DEE4-482C-B9FD-6DB0200B79E5}"
 EndProject
 EndProject
@@ -32,7 +32,7 @@ EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GitHub", "GitHub", "{13BB2C46-B324-4B9C-92EB-CE6184D4736E}"
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GitHub", "GitHub", "{13BB2C46-B324-4B9C-92EB-CE6184D4736E}"
 	ProjectSection(SolutionItems) = preProject
 	ProjectSection(SolutionItems) = preProject
 		.github\workflows\api-docs.yml = .github\workflows\api-docs.yml
 		.github\workflows\api-docs.yml = .github\workflows\api-docs.yml
-		.github\workflows\build-release.yml = .github\workflows\build-release.yml
+		.github\workflows\build.yml = .github\workflows\build.yml
 		.github\workflows\check-duplicates.yml = .github\workflows\check-duplicates.yml
 		.github\workflows\check-duplicates.yml = .github\workflows\check-duplicates.yml
 		copilot-instructions.md = copilot-instructions.md
 		copilot-instructions.md = copilot-instructions.md
 		GitVersion.yml = GitVersion.yml
 		GitVersion.yml = GitVersion.yml
@@ -47,6 +47,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{C7A51224-5
 		.github\CODEOWNERS = .github\CODEOWNERS
 		.github\CODEOWNERS = .github\CODEOWNERS
 		CODE_OF_CONDUCT.md = CODE_OF_CONDUCT.md
 		CODE_OF_CONDUCT.md = CODE_OF_CONDUCT.md
 		CONTRIBUTING.md = CONTRIBUTING.md
 		CONTRIBUTING.md = CONTRIBUTING.md
+		.github\copilot-instructions.md = .github\copilot-instructions.md
 		README.md = README.md
 		README.md = README.md
 	EndProjectSection
 	EndProjectSection
 EndProject
 EndProject

+ 8 - 3
Tests/StressTests/ApplicationStressTests.cs

@@ -17,9 +17,7 @@ public class ApplicationStressTests : TestsAllViews
 
 
     private const int NUM_PASSES = 50;
     private const int NUM_PASSES = 50;
     private const int NUM_INCREMENTS = 500;
     private const int NUM_INCREMENTS = 500;
-    
-    // Use longer timeout when running under debugger to account for slower iterations
-    private static readonly int POLL_MS = System.Diagnostics.Debugger.IsAttached ? 500 : 100;
+    private const int POLL_MS = 100;
 
 
     /// <summary>
     /// <summary>
     /// Stress test for Application.Invoke to verify that invocations from background threads
     /// Stress test for Application.Invoke to verify that invocations from background threads
@@ -79,6 +77,13 @@ public class ApplicationStressTests : TestsAllViews
                 while (_tbCounter != (j + 1) * numIncrements) // Wait for tbCounter to reach expected value
                 while (_tbCounter != (j + 1) * numIncrements) // Wait for tbCounter to reach expected value
                 {
                 {
                     int tbNow = _tbCounter;
                     int tbNow = _tbCounter;
+
+                    // Wait for Application.Top to be running to ensure timed events can be processed
+                    while (Application.Top is null || Application.Top is { Running: false })
+                    {
+                        Thread.Sleep (1);
+                    }
+
                     _wakeUp.Wait (pollMs);
                     _wakeUp.Wait (pollMs);
 
 
                     if (_tbCounter != tbNow)
                     if (_tbCounter != tbNow)

+ 7 - 4
Tests/TerminalGuiFluentTesting/GuiTestContext.cs

@@ -13,7 +13,7 @@ namespace TerminalGuiFluentTesting;
 public class GuiTestContext : IDisposable
 public class GuiTestContext : IDisposable
 {
 {
     private readonly CancellationTokenSource _cts = new ();
     private readonly CancellationTokenSource _cts = new ();
-    private readonly CancellationTokenSource _hardStop = new (With.Timeout);
+    private readonly CancellationTokenSource _hardStop;
     private readonly Task _runTask;
     private readonly Task _runTask;
     private Exception? _ex;
     private Exception? _ex;
     private readonly FakeOutput _output = new ();
     private readonly FakeOutput _output = new ();
@@ -25,9 +25,12 @@ public class GuiTestContext : IDisposable
     private readonly TestDriver _driver;
     private readonly TestDriver _driver;
     private bool _finished;
     private bool _finished;
     private readonly FakeSizeMonitor _fakeSizeMonitor;
     private readonly FakeSizeMonitor _fakeSizeMonitor;
+    private readonly TimeSpan _timeout;
 
 
-    internal GuiTestContext (Func<Toplevel> topLevelBuilder, int width, int height, TestDriver driver, TextWriter? logWriter = null)
+    internal GuiTestContext (Func<Toplevel> topLevelBuilder, int width, int height, TestDriver driver, TextWriter? logWriter = null, TimeSpan? timeout = null)
     {
     {
+        _timeout = timeout ?? TimeSpan.FromSeconds (30);
+        _hardStop = new (_timeout);
         // Remove frame limit
         // Remove frame limit
         Application.MaximumIterationsPerSecond = ushort.MaxValue;
         Application.MaximumIterationsPerSecond = ushort.MaxValue;
 
 
@@ -104,7 +107,7 @@ public class GuiTestContext : IDisposable
                              _cts.Token);
                              _cts.Token);
 
 
         // Wait for booting to complete with a timeout to avoid hangs
         // Wait for booting to complete with a timeout to avoid hangs
-        if (!booting.WaitAsync (TimeSpan.FromSeconds (10)).Result)
+        if (!booting.WaitAsync (_timeout).Result)
         {
         {
             throw new TimeoutException ("Application failed to start within the allotted time.");
             throw new TimeoutException ("Application failed to start within the allotted time.");
         }
         }
@@ -440,7 +443,7 @@ public class GuiTestContext : IDisposable
 
 
         while (!condition ())
         while (!condition ())
         {
         {
-            if (sw.Elapsed > With.Timeout)
+            if (sw.Elapsed > _timeout)
             {
             {
                 throw new TimeoutException ("Failed to reach condition within the time limit");
                 throw new TimeoutException ("Failed to reach condition within the time limit");
             }
             }

+ 2 - 2
Tests/TerminalGuiFluentTesting/With.cs

@@ -16,7 +16,7 @@ public static class With
     /// <returns></returns>
     /// <returns></returns>
     public static GuiTestContext A<T> (int width, int height, TestDriver testDriver, TextWriter? logWriter = null) where T : Toplevel, new ()
     public static GuiTestContext A<T> (int width, int height, TestDriver testDriver, TextWriter? logWriter = null) where T : Toplevel, new ()
     {
     {
-        return new (() => new T (), width, height,testDriver,logWriter);
+        return new (() => new T (), width, height, testDriver, logWriter, Timeout);
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -29,7 +29,7 @@ public static class With
     /// <returns></returns>
     /// <returns></returns>
     public static GuiTestContext A (Func<Toplevel> toplevelFactory, int width, int height, TestDriver testDriver)
     public static GuiTestContext A (Func<Toplevel> toplevelFactory, int width, int height, TestDriver testDriver)
     {
     {
-        return new (toplevelFactory, width, height, testDriver);
+        return new (toplevelFactory, width, height, testDriver, null, Timeout);
     }
     }
     /// <summary>
     /// <summary>
     ///     The global timeout to allow for any given application to run for before shutting down.
     ///     The global timeout to allow for any given application to run for before shutting down.

+ 48 - 0
Tests/UnitTests/Application/ApplicationImplTests.cs

@@ -583,4 +583,52 @@ public class ApplicationImplTests
 
 
         Assert.True (result);
         Assert.True (result);
     }
     }
+
+    [Fact]
+    public void ApplicationImpl_UsesInstanceFields_NotStaticReferences()
+    {
+        // This test verifies that ApplicationImpl uses instance fields instead of static Application references
+        var orig = ApplicationImpl.Instance;
+
+        var v2 = NewApplicationImpl();
+        ApplicationImpl.ChangeInstance(v2);
+
+        // Before Init, all fields should be null/default
+        Assert.Null(v2.Driver);
+        Assert.False(v2.Initialized);
+        Assert.Null(v2.Popover);
+        Assert.Null(v2.Navigation);
+        Assert.Null(v2.Top);
+        Assert.Empty(v2.TopLevels);
+
+        // Init should populate instance fields
+        v2.Init();
+
+        // After Init, Driver, Navigation, and Popover should be populated
+        Assert.NotNull(v2.Driver);
+        Assert.True(v2.Initialized);
+        Assert.NotNull(v2.Popover);
+        Assert.NotNull(v2.Navigation);
+        Assert.Null(v2.Top); // Top is still null until Run
+
+        // Verify that static Application properties delegate to instance
+        Assert.Equal(v2.Driver, Application.Driver);
+        Assert.Equal(v2.Initialized, Application.Initialized);
+        Assert.Equal(v2.Popover, Application.Popover);
+        Assert.Equal(v2.Navigation, Application.Navigation);
+        Assert.Equal(v2.Top, Application.Top);
+        Assert.Same(v2.TopLevels, Application.TopLevels);
+
+        // Shutdown should clean up instance fields
+        v2.Shutdown();
+
+        Assert.Null(v2.Driver);
+        Assert.False(v2.Initialized);
+        Assert.Null(v2.Popover);
+        Assert.Null(v2.Navigation);
+        Assert.Null(v2.Top);
+        Assert.Empty(v2.TopLevels);
+
+        ApplicationImpl.ChangeInstance(orig);
+    }
 }
 }

+ 5 - 6
Tests/UnitTests/Application/ApplicationTests.cs

@@ -309,7 +309,7 @@ public class ApplicationTests
 
 
             // Public Properties
             // Public Properties
             Assert.Null (Application.Top);
             Assert.Null (Application.Top);
-            Assert.Null (Application.MouseGrabHandler.MouseGrabView);
+            Assert.Null (Application.Mouse.MouseGrabView);
 
 
             // Don't check Application.ForceDriver
             // Don't check Application.ForceDriver
             // Assert.Empty (Application.ForceDriver);
             // Assert.Empty (Application.ForceDriver);
@@ -574,7 +574,7 @@ public class ApplicationTests
         Assert.Null (Application.Top);
         Assert.Null (Application.Top);
         RunState rs = Application.Begin (new ());
         RunState rs = Application.Begin (new ());
         Assert.Equal (Application.Top, rs.Toplevel);
         Assert.Equal (Application.Top, rs.Toplevel);
-        Assert.Null (Application.MouseGrabHandler.MouseGrabView); // public
+        Assert.Null (Application.Mouse.MouseGrabView); // public
         Application.Top!.Dispose ();
         Application.Top!.Dispose ();
     }
     }
 
 
@@ -584,13 +584,12 @@ public class ApplicationTests
     [AutoInitShutdown]
     [AutoInitShutdown]
     public void Invoke_Adds_Idle ()
     public void Invoke_Adds_Idle ()
     {
     {
-        var top = new Toplevel ();
+        Toplevel top = new ();
         RunState rs = Application.Begin (top);
         RunState rs = Application.Begin (top);
-        var firstIteration = false;
 
 
         var actionCalled = 0;
         var actionCalled = 0;
         Application.Invoke (() => { actionCalled++; });
         Application.Invoke (() => { actionCalled++; });
-        Application.RunIteration (ref rs, firstIteration);
+        ApplicationImpl.Instance.TimedEvents!.RunTimers ();
         Assert.Equal (1, actionCalled);
         Assert.Equal (1, actionCalled);
         top.Dispose ();
         top.Dispose ();
         Application.Shutdown ();
         Application.Shutdown ();
@@ -932,7 +931,7 @@ public class ApplicationTests
         Assert.Equal (new (0, 0), w.Frame.Location);
         Assert.Equal (new (0, 0), w.Frame.Location);
 
 
         Application.RaiseMouseEvent (new () { Flags = MouseFlags.Button1Pressed });
         Application.RaiseMouseEvent (new () { Flags = MouseFlags.Button1Pressed });
-        Assert.Equal (w.Border, Application.MouseGrabHandler.MouseGrabView);
+        Assert.Equal (w.Border, Application.Mouse.MouseGrabView);
         Assert.Equal (new (0, 0), w.Frame.Location);
         Assert.Equal (new (0, 0), w.Frame.Location);
 
 
         // Move down and to the right.
         // Move down and to the right.

+ 98 - 0
Tests/UnitTests/Application/MainLoopTests.cs

@@ -1,4 +1,5 @@
 using System.Diagnostics;
 using System.Diagnostics;
+using Xunit.Abstractions;
 
 
 // Alias Console to MockConsole so we don't accidentally use Console
 // Alias Console to MockConsole so we don't accidentally use Console
 
 
@@ -7,6 +8,14 @@ namespace UnitTests.ApplicationTests;
 /// <summary>Tests MainLoop using the FakeMainLoop.</summary>
 /// <summary>Tests MainLoop using the FakeMainLoop.</summary>
 public class MainLoopTests
 public class MainLoopTests
 {
 {
+    private readonly ITestOutputHelper _output;
+
+    public MainLoopTests (ITestOutputHelper output)
+    {
+        _output = output;
+        ConsoleDriver.RunningUnitTests = true;
+    }
+
     private static Button btn;
     private static Button btn;
     private static string cancel;
     private static string cancel;
     private static string clickMe;
     private static string clickMe;
@@ -708,6 +717,95 @@ public class MainLoopTests
         Assert.Equal (10, functionCalled);
         Assert.Equal (10, functionCalled);
     }
     }
 
 
+    [Theory]
+    [InlineData ("fake")]
+    [InlineData ("windows")]
+    [InlineData ("dotnet")]
+    [InlineData ("unix")]
+    public void Application_Invoke_Run_TimedEvents (string driverName)
+    {
+        // Arrange
+        Application.Init (driverName: driverName);
+        var functionCalled = 0;
+        var stopwatch = new Stopwatch ();
+
+        // Act
+        Application.Invoke (() =>
+                            {
+                                // Stop the stopwatch *after* the function is called.
+                                functionCalled++;
+                                stopwatch.Stop ();
+                                Application.RequestStop ();
+                            });
+
+        // Start timing just before running the application loop.
+        stopwatch.Start ();
+        Application.Run ();
+
+        // Assert
+        Assert.NotNull (Application.Top);
+        Application.Top.Dispose ();
+        Application.Shutdown ();
+        Assert.Equal (1, functionCalled);
+
+        // Output the elapsed time for this test case.
+        // ReSharper disable once Xunit.XunitTestWithConsoleOutput
+        // ReSharper disable once LocalizableElement
+        Console.WriteLine ($"[{driverName}] Duration: {stopwatch.Elapsed.TotalMilliseconds:F2} ms");
+
+        // Output elapsed duration to xUnit's test output
+        _output.WriteLine ($"[{driverName}] Duration: {stopwatch.Elapsed.TotalMilliseconds:F2} ms");
+    }
+
+    [Theory]
+    [InlineData ("fake")]
+    [InlineData ("windows")]
+    [InlineData ("dotnet")]
+    [InlineData ("unix")]
+    public void Application_AddTimeout_Run_TimedEvents (string driverName)
+    {
+        // Arrange
+        Application.Init (driverName: driverName);
+        var functionCalled = 0;
+        var stopwatch = new Stopwatch ();
+
+        // Act
+        bool Function ()
+        {
+            functionCalled++;
+
+            if (functionCalled == 10 && Application.Top is { Running: true })
+            {
+                stopwatch.Stop ();
+                Application.RequestStop ();
+
+                return false;
+            }
+
+            return true;
+        }
+
+        Application.AddTimeout (TimeSpan.FromMilliseconds (1), Function);
+
+        // Start timing just before running the application loop.
+        stopwatch.Start ();
+        Application.Run ();
+
+        // Assert
+        Assert.NotNull (Application.Top);
+        Application.Top.Dispose ();
+        Application.Shutdown ();
+        Assert.Equal (10, functionCalled);
+
+        // Output the elapsed time for this test case.
+        // ReSharper disable once Xunit.XunitTestWithConsoleOutput
+        // ReSharper disable once LocalizableElement
+        Console.WriteLine ($"[{driverName}] Duration: {stopwatch.Elapsed.TotalMilliseconds:F2} ms");
+
+        // Output elapsed duration to xUnit's test output
+        _output.WriteLine ($"[{driverName}] Duration: {stopwatch.Elapsed.TotalMilliseconds:F2} ms");
+    }
+
     public static IEnumerable<object []> TestAddTimeout
     public static IEnumerable<object []> TestAddTimeout
     {
     {
         get
         get

+ 31 - 31
Tests/UnitTests/Application/Mouse/ApplicationMouseTests.cs

@@ -260,39 +260,39 @@ public class ApplicationMouseTests
         //                             if (iterations == 0)
         //                             if (iterations == 0)
         //                             {
         //                             {
         //                                 Assert.True (tf.HasFocus);
         //                                 Assert.True (tf.HasFocus);
-        //                                 Assert.Null (Application.MouseGrabHandler.MouseGrabView);
+        //                                 Assert.Null (Application.Mouse.MouseGrabView);
 
 
         //                                 Application.RaiseMouseEvent (new () { ScreenPosition = new (5, 5), Flags = MouseFlags.ReportMousePosition });
         //                                 Application.RaiseMouseEvent (new () { ScreenPosition = new (5, 5), Flags = MouseFlags.ReportMousePosition });
 
 
-        //                                 Assert.Equal (sv, Application.MouseGrabHandler.MouseGrabView);
+        //                                 Assert.Equal (sv, Application.Mouse.MouseGrabView);
 
 
         //                                 MessageBox.Query ("Title", "Test", "Ok");
         //                                 MessageBox.Query ("Title", "Test", "Ok");
 
 
-        //                                 Assert.Null (Application.MouseGrabHandler.MouseGrabView);
+        //                                 Assert.Null (Application.Mouse.MouseGrabView);
         //                             }
         //                             }
         //                             else if (iterations == 1)
         //                             else if (iterations == 1)
         //                             {
         //                             {
-        //                                 // Application.MouseGrabHandler.MouseGrabView is null because
+        //                                 // Application.Mouse.MouseGrabView is null because
         //                                 // another toplevel (Dialog) was opened
         //                                 // another toplevel (Dialog) was opened
-        //                                 Assert.Null (Application.MouseGrabHandler.MouseGrabView);
+        //                                 Assert.Null (Application.Mouse.MouseGrabView);
 
 
         //                                 Application.RaiseMouseEvent (new () { ScreenPosition = new (5, 5), Flags = MouseFlags.ReportMousePosition });
         //                                 Application.RaiseMouseEvent (new () { ScreenPosition = new (5, 5), Flags = MouseFlags.ReportMousePosition });
 
 
-        //                                 Assert.Null (Application.MouseGrabHandler.MouseGrabView);
+        //                                 Assert.Null (Application.Mouse.MouseGrabView);
 
 
         //                                 Application.RaiseMouseEvent (new () { ScreenPosition = new (40, 12), Flags = MouseFlags.ReportMousePosition });
         //                                 Application.RaiseMouseEvent (new () { ScreenPosition = new (40, 12), Flags = MouseFlags.ReportMousePosition });
 
 
-        //                                 Assert.Null (Application.MouseGrabHandler.MouseGrabView);
+        //                                 Assert.Null (Application.Mouse.MouseGrabView);
 
 
         //                                 Application.RaiseMouseEvent (new () { ScreenPosition = new (0, 0), Flags = MouseFlags.Button1Pressed });
         //                                 Application.RaiseMouseEvent (new () { ScreenPosition = new (0, 0), Flags = MouseFlags.Button1Pressed });
 
 
-        //                                 Assert.Null (Application.MouseGrabHandler.MouseGrabView);
+        //                                 Assert.Null (Application.Mouse.MouseGrabView);
 
 
         //                                 Application.RequestStop ();
         //                                 Application.RequestStop ();
         //                             }
         //                             }
         //                             else if (iterations == 2)
         //                             else if (iterations == 2)
         //                             {
         //                             {
-        //                                 Assert.Null (Application.MouseGrabHandler.MouseGrabView);
+        //                                 Assert.Null (Application.Mouse.MouseGrabView);
 
 
         //                                 Application.RequestStop ();
         //                                 Application.RequestStop ();
         //                             }
         //                             }
@@ -313,33 +313,33 @@ public class ApplicationMouseTests
         var view2 = new View { Id = "view2" };
         var view2 = new View { Id = "view2" };
         var view3 = new View { Id = "view3" };
         var view3 = new View { Id = "view3" };
 
 
-        Application.MouseGrabHandler.GrabbedMouse += Application_GrabbedMouse;
-        Application.MouseGrabHandler.UnGrabbedMouse += Application_UnGrabbedMouse;
+        Application.Mouse.GrabbedMouse += Application_GrabbedMouse;
+        Application.Mouse.UnGrabbedMouse += Application_UnGrabbedMouse;
 
 
-        Application.MouseGrabHandler.GrabMouse (view1);
+        Application.Mouse.GrabMouse (view1);
         Assert.Equal (0, count);
         Assert.Equal (0, count);
         Assert.Equal (grabView, view1);
         Assert.Equal (grabView, view1);
-        Assert.Equal (view1, Application.MouseGrabHandler.MouseGrabView);
+        Assert.Equal (view1, Application.Mouse.MouseGrabView);
 
 
-        Application.MouseGrabHandler.UngrabMouse ();
+        Application.Mouse.UngrabMouse ();
         Assert.Equal (1, count);
         Assert.Equal (1, count);
         Assert.Equal (grabView, view1);
         Assert.Equal (grabView, view1);
-        Assert.Null (Application.MouseGrabHandler.MouseGrabView);
+        Assert.Null (Application.Mouse.MouseGrabView);
 
 
-        Application.MouseGrabHandler.GrabbedMouse += Application_GrabbedMouse;
-        Application.MouseGrabHandler.UnGrabbedMouse += Application_UnGrabbedMouse;
+        Application.Mouse.GrabbedMouse += Application_GrabbedMouse;
+        Application.Mouse.UnGrabbedMouse += Application_UnGrabbedMouse;
 
 
-        Application.MouseGrabHandler.GrabMouse (view2);
+        Application.Mouse.GrabMouse (view2);
         Assert.Equal (1, count);
         Assert.Equal (1, count);
         Assert.Equal (grabView, view2);
         Assert.Equal (grabView, view2);
-        Assert.Equal (view2, Application.MouseGrabHandler.MouseGrabView);
+        Assert.Equal (view2, Application.Mouse.MouseGrabView);
 
 
-        Application.MouseGrabHandler.UngrabMouse ();
+        Application.Mouse.UngrabMouse ();
         Assert.Equal (2, count);
         Assert.Equal (2, count);
         Assert.Equal (grabView, view2);
         Assert.Equal (grabView, view2);
-        Assert.Equal (view3, Application.MouseGrabHandler.MouseGrabView);
-        Application.MouseGrabHandler.UngrabMouse ();
-        Assert.Null (Application.MouseGrabHandler.MouseGrabView);
+        Assert.Equal (view3, Application.Mouse.MouseGrabView);
+        Application.Mouse.UngrabMouse ();
+        Assert.Null (Application.Mouse.MouseGrabView);
 
 
         void Application_GrabbedMouse (object sender, ViewEventArgs e)
         void Application_GrabbedMouse (object sender, ViewEventArgs e)
         {
         {
@@ -354,7 +354,7 @@ public class ApplicationMouseTests
                 grabView = view2;
                 grabView = view2;
             }
             }
 
 
-            Application.MouseGrabHandler.GrabbedMouse -= Application_GrabbedMouse;
+            Application.Mouse.GrabbedMouse -= Application_GrabbedMouse;
         }
         }
 
 
         void Application_UnGrabbedMouse (object sender, ViewEventArgs e)
         void Application_UnGrabbedMouse (object sender, ViewEventArgs e)
@@ -375,10 +375,10 @@ public class ApplicationMouseTests
             if (count > 1)
             if (count > 1)
             {
             {
                 // It's possible to grab another view after the previous was ungrabbed
                 // It's possible to grab another view after the previous was ungrabbed
-                Application.MouseGrabHandler.GrabMouse (view3);
+                Application.Mouse.GrabMouse (view3);
             }
             }
 
 
-            Application.MouseGrabHandler.UnGrabbedMouse -= Application_UnGrabbedMouse;
+            Application.Mouse.UnGrabbedMouse -= Application_UnGrabbedMouse;
         }
         }
     }
     }
 
 
@@ -393,18 +393,18 @@ public class ApplicationMouseTests
         top.Add (view);
         top.Add (view);
         Application.Begin (top);
         Application.Begin (top);
 
 
-        Assert.Null (Application.MouseGrabHandler.MouseGrabView);
-        Application.MouseGrabHandler.GrabMouse (view);
-        Assert.Equal (view, Application.MouseGrabHandler.MouseGrabView);
+        Assert.Null (Application.Mouse.MouseGrabView);
+        Application.Mouse.GrabMouse (view);
+        Assert.Equal (view, Application.Mouse.MouseGrabView);
         top.Remove (view);
         top.Remove (view);
-        Application.MouseGrabHandler.UngrabMouse ();
+        Application.Mouse.UngrabMouse ();
         view.Dispose ();
         view.Dispose ();
 #if DEBUG_IDISPOSABLE
 #if DEBUG_IDISPOSABLE
         Assert.True (view.WasDisposed);
         Assert.True (view.WasDisposed);
 #endif
 #endif
 
 
         Application.RaiseMouseEvent (new () { ScreenPosition = new (0, 0), Flags = MouseFlags.Button1Pressed });
         Application.RaiseMouseEvent (new () { ScreenPosition = new (0, 0), Flags = MouseFlags.Button1Pressed });
-        Assert.Null (Application.MouseGrabHandler.MouseGrabView);
+        Assert.Null (Application.Mouse.MouseGrabView);
         Assert.Equal (0, count);
         Assert.Equal (0, count);
         top.Dispose ();
         top.Dispose ();
     }
     }

+ 9 - 13
Tests/UnitTests/Dialogs/WizardTests.cs

@@ -20,8 +20,8 @@ public class WizardTests ()
     public void Finish_Button_Closes ()
     public void Finish_Button_Closes ()
     {
     {
         // https://github.com/gui-cs/Terminal.Gui/issues/1833
         // https://github.com/gui-cs/Terminal.Gui/issues/1833
-        var wizard = new Wizard ();
-        var step1 = new WizardStep { Title = "step1" };
+        Wizard wizard = new ();
+        WizardStep step1 = new () { Title = "step1" };
         wizard.AddStep (step1);
         wizard.AddStep (step1);
 
 
         var finishedFired = false;
         var finishedFired = false;
@@ -31,11 +31,10 @@ public class WizardTests ()
         wizard.Closed += (s, e) => { closedFired = true; };
         wizard.Closed += (s, e) => { closedFired = true; };
 
 
         RunState runstate = Application.Begin (wizard);
         RunState runstate = Application.Begin (wizard);
-        var firstIteration = true;
-        Application.RunIteration (ref runstate, firstIteration);
+        AutoInitShutdownAttribute.RunIteration ();
 
 
         wizard.NextFinishButton.InvokeCommand (Command.Accept);
         wizard.NextFinishButton.InvokeCommand (Command.Accept);
-        Application.RunIteration (ref runstate, firstIteration);
+        AutoInitShutdownAttribute.RunIteration ();
         Application.End (runstate);
         Application.End (runstate);
         Assert.True (finishedFired);
         Assert.True (finishedFired);
         Assert.True (closedFired);
         Assert.True (closedFired);
@@ -44,10 +43,9 @@ public class WizardTests ()
 
 
         // Same test, but with two steps
         // Same test, but with two steps
         wizard = new ();
         wizard = new ();
-        firstIteration = false;
         step1 = new() { Title = "step1" };
         step1 = new() { Title = "step1" };
         wizard.AddStep (step1);
         wizard.AddStep (step1);
-        var step2 = new WizardStep { Title = "step2" };
+        WizardStep step2 = new () { Title = "step2" };
         wizard.AddStep (step2);
         wizard.AddStep (step2);
 
 
         finishedFired = false;
         finishedFired = false;
@@ -57,7 +55,7 @@ public class WizardTests ()
         wizard.Closed += (s, e) => { closedFired = true; };
         wizard.Closed += (s, e) => { closedFired = true; };
 
 
         runstate = Application.Begin (wizard);
         runstate = Application.Begin (wizard);
-        Application.RunIteration (ref runstate, firstIteration);
+        AutoInitShutdownAttribute.RunIteration ();
 
 
         Assert.Equal (step1.Title, wizard.CurrentStep.Title);
         Assert.Equal (step1.Title, wizard.CurrentStep.Title);
         wizard.NextFinishButton.InvokeCommand (Command.Accept);
         wizard.NextFinishButton.InvokeCommand (Command.Accept);
@@ -77,7 +75,6 @@ public class WizardTests ()
 
 
         // Same test, but with two steps but the 1st one disabled
         // Same test, but with two steps but the 1st one disabled
         wizard = new ();
         wizard = new ();
-        firstIteration = false;
         step1 = new() { Title = "step1" };
         step1 = new() { Title = "step1" };
         wizard.AddStep (step1);
         wizard.AddStep (step1);
         step2 = new() { Title = "step2" };
         step2 = new() { Title = "step2" };
@@ -91,7 +88,7 @@ public class WizardTests ()
         wizard.Closed += (s, e) => { closedFired = true; };
         wizard.Closed += (s, e) => { closedFired = true; };
 
 
         runstate = Application.Begin (wizard);
         runstate = Application.Begin (wizard);
-        Application.RunIteration (ref runstate, firstIteration);
+        AutoInitShutdownAttribute.RunIteration ();
 
 
         Assert.Equal (step2.Title, wizard.CurrentStep.Title);
         Assert.Equal (step2.Title, wizard.CurrentStep.Title);
         Assert.Equal (wizard.GetLastStep ().Title, wizard.CurrentStep.Title);
         Assert.Equal (wizard.GetLastStep ().Title, wizard.CurrentStep.Title);
@@ -458,13 +455,12 @@ public class WizardTests ()
                 Glyphs.LRCornerDbl
                 Glyphs.LRCornerDbl
             }";
             }";
 
 
-        var wizard = new Wizard { Title = title, Width = width, Height = height };
+        Wizard wizard = new () { Title = title, Width = width, Height = height };
         wizard.AddStep (new() { Title = stepTitle });
         wizard.AddStep (new() { Title = stepTitle });
 
 
         //wizard.LayoutSubViews ();
         //wizard.LayoutSubViews ();
-        var firstIteration = false;
         RunState runstate = Application.Begin (wizard);
         RunState runstate = Application.Begin (wizard);
-        Application.RunIteration (ref runstate, firstIteration);
+        AutoInitShutdownAttribute.RunIteration ();
 
 
         // TODO: Disabled until Dim.Auto is used in Dialog
         // TODO: Disabled until Dim.Auto is used in Dialog
         //DriverAsserts.AssertDriverContentsWithFrameAre (
         //DriverAsserts.AssertDriverContentsWithFrameAre (

+ 2 - 2
Tests/UnitTests/View/Adornment/BorderTests.cs

@@ -222,7 +222,7 @@ public class BorderTests (ITestOutputHelper output)
     [InlineData (10)]
     [InlineData (10)]
     public void Border_With_Title_Border_Double_Thickness_Top_Three_Size_Width (int width)
     public void Border_With_Title_Border_Double_Thickness_Top_Three_Size_Width (int width)
     {
     {
-        var win = new Window
+        Window win = new ()
         {
         {
             Title = "1234", Width = Dim.Fill (), Height = Dim.Fill (), BorderStyle = LineStyle.Double
             Title = "1234", Width = Dim.Fill (), Height = Dim.Fill (), BorderStyle = LineStyle.Double
         };
         };
@@ -231,7 +231,7 @@ public class BorderTests (ITestOutputHelper output)
         RunState rs = Application.Begin (win);
         RunState rs = Application.Begin (win);
 
 
         AutoInitShutdownAttribute.FakeResize(new Size(width, 4));
         AutoInitShutdownAttribute.FakeResize(new Size(width, 4));
-        Application.RunIteration (ref rs, false);
+        AutoInitShutdownAttribute.RunIteration ();
         var expected = string.Empty;
         var expected = string.Empty;
 
 
         switch (width)
         switch (width)

+ 1 - 1
Tests/UnitTests/View/Adornment/ShadowStyleTests.cs

@@ -160,7 +160,7 @@ public class ShadowStyleTests (ITestOutputHelper output)
         view.NewMouseEvent (new () { Flags = MouseFlags.Button1Released, Position = new (0, 0) });
         view.NewMouseEvent (new () { Flags = MouseFlags.Button1Released, Position = new (0, 0) });
         Assert.Equal (origThickness, view.Margin.Thickness);
         Assert.Equal (origThickness, view.Margin.Thickness);
 
 
-        // Button1Pressed, Button1Released cause Application.MouseGrabHandler.MouseGrabView to be set
+        // Button1Pressed, Button1Released cause Application.Mouse.MouseGrabView to be set
         Application.ResetState (true);
         Application.ResetState (true);
     }
     }
 }
 }

+ 3 - 3
Tests/UnitTests/View/Draw/ClearViewportTests.cs

@@ -321,7 +321,7 @@ public class ClearViewportTests (ITestOutputHelper output)
     {
     {
         ConfigurationManager.Enable (ConfigLocations.LibraryResources);
         ConfigurationManager.Enable (ConfigLocations.LibraryResources);
 
 
-        var root = new View { Width = 20, Height = 10 };
+        View root = new () { Width = 20, Height = 10 };
 
 
         string text = new ('c', 100);
         string text = new ('c', 100);
 
 
@@ -335,10 +335,10 @@ public class ClearViewportTests (ITestOutputHelper output)
 
 
         root.Add (v);
         root.Add (v);
 
 
-        var top = new Toplevel ();
+        Toplevel top = new ();
         top.Add (root);
         top.Add (root);
         RunState runState = Application.Begin (top);
         RunState runState = Application.Begin (top);
-        Application.RunIteration (ref runState);
+        AutoInitShutdownAttribute.RunIteration ();
 
 
         if (label)
         if (label)
         {
         {

+ 8 - 8
Tests/UnitTests/View/Mouse/MouseTests.cs

@@ -96,7 +96,7 @@ public class MouseTests : TestsAllViews
 
 
         view.Dispose ();
         view.Dispose ();
 
 
-        // Button1Pressed, Button1Released cause Application.MouseGrabHandler.MouseGrabView to be set
+        // Button1Pressed, Button1Released cause Application.Mouse.MouseGrabView to be set
         Application.ResetState (true);
         Application.ResetState (true);
     }
     }
 
 
@@ -126,7 +126,7 @@ public class MouseTests : TestsAllViews
 
 
         view.Dispose ();
         view.Dispose ();
 
 
-        // Button1Pressed, Button1Released cause Application.MouseGrabHandler.MouseGrabView to be set
+        // Button1Pressed, Button1Released cause Application.Mouse.MouseGrabView to be set
         Application.ResetState (true);
         Application.ResetState (true);
     }
     }
 
 
@@ -156,7 +156,7 @@ public class MouseTests : TestsAllViews
 
 
         view.Dispose ();
         view.Dispose ();
 
 
-        // Button1Pressed, Button1Released cause Application.MouseGrabHandler.MouseGrabView to be set
+        // Button1Pressed, Button1Released cause Application.Mouse.MouseGrabView to be set
         Application.ResetState (true);
         Application.ResetState (true);
     }
     }
 
 
@@ -377,7 +377,7 @@ public class MouseTests : TestsAllViews
 
 
     //    testView.Dispose ();
     //    testView.Dispose ();
 
 
-    //    // Button1Pressed, Button1Released cause Application.MouseGrabHandler.MouseGrabView to be set
+    //    // Button1Pressed, Button1Released cause Application.Mouse.MouseGrabView to be set
     //    Application.ResetState (true);
     //    Application.ResetState (true);
 
 
     //}
     //}
@@ -442,7 +442,7 @@ public class MouseTests : TestsAllViews
 
 
         testView.Dispose ();
         testView.Dispose ();
 
 
-        // Button1Pressed, Button1Released cause Application.MouseGrabHandler.MouseGrabView to be set
+        // Button1Pressed, Button1Released cause Application.Mouse.MouseGrabView to be set
         Application.ResetState (true);
         Application.ResetState (true);
     }
     }
 
 
@@ -504,7 +504,7 @@ public class MouseTests : TestsAllViews
 
 
         testView.Dispose ();
         testView.Dispose ();
 
 
-        // Button1Pressed, Button1Released cause Application.MouseGrabHandler.MouseGrabView to be set
+        // Button1Pressed, Button1Released cause Application.Mouse.MouseGrabView to be set
         Application.ResetState (true);
         Application.ResetState (true);
     }
     }
 
 
@@ -567,7 +567,7 @@ public class MouseTests : TestsAllViews
 
 
         testView.Dispose ();
         testView.Dispose ();
 
 
-        // Button1Pressed, Button1Released cause Application.MouseGrabHandler.MouseGrabView to be set
+        // Button1Pressed, Button1Released cause Application.Mouse.MouseGrabView to be set
         Application.ResetState (true);
         Application.ResetState (true);
     }
     }
 
 
@@ -631,7 +631,7 @@ public class MouseTests : TestsAllViews
 
 
         testView.Dispose ();
         testView.Dispose ();
 
 
-        // Button1Pressed, Button1Released cause Application.MouseGrabHandler.MouseGrabView to be set
+        // Button1Pressed, Button1Released cause Application.Mouse.MouseGrabView to be set
         Application.ResetState (true);
         Application.ResetState (true);
     }
     }
     private class MouseEventTestView : View
     private class MouseEventTestView : View

+ 6 - 6
Tests/UnitTests/Views/LabelTests.cs

@@ -91,20 +91,20 @@ public class LabelTests (ITestOutputHelper output)
     [AutoInitShutdown]
     [AutoInitShutdown]
     public void Label_Draw_Fill_Remaining ()
     public void Label_Draw_Fill_Remaining ()
     {
     {
-        var tfSize = new Size (80, 1);
+        Size tfSize = new (80, 1);
 
 
-        var label = new Label { Text = "This label needs to be cleared before rewritten.", Width = tfSize.Width, Height = tfSize.Height };
+        Label label = new () { Text = "This label needs to be cleared before rewritten.", Width = tfSize.Width, Height = tfSize.Height };
 
 
-        var tf1 = new TextFormatter { Direction = TextDirection.LeftRight_TopBottom, ConstrainToSize = tfSize };
+        TextFormatter tf1 = new () { Direction = TextDirection.LeftRight_TopBottom, ConstrainToSize = tfSize };
         tf1.Text = "This TextFormatter (tf1) without fill will not be cleared on rewritten.";
         tf1.Text = "This TextFormatter (tf1) without fill will not be cleared on rewritten.";
 
 
-        var tf2 = new TextFormatter { Direction = TextDirection.LeftRight_TopBottom, ConstrainToSize = tfSize, FillRemaining = true };
+        TextFormatter tf2 = new () { Direction = TextDirection.LeftRight_TopBottom, ConstrainToSize = tfSize, FillRemaining = true };
         tf2.Text = "This TextFormatter (tf2) with fill will be cleared on rewritten.";
         tf2.Text = "This TextFormatter (tf2) with fill will be cleared on rewritten.";
 
 
-        var top = new Toplevel ();
+        Toplevel top = new ();
         top.Add (label);
         top.Add (label);
         RunState runState = Application.Begin (top);
         RunState runState = Application.Begin (top);
-        Application.RunIteration (ref runState);
+        AutoInitShutdownAttribute.RunIteration ();
 
 
         Assert.False (label.TextFormatter.FillRemaining);
         Assert.False (label.TextFormatter.FillRemaining);
         Assert.False (tf1.FillRemaining);
         Assert.False (tf1.FillRemaining);

+ 10 - 11
Tests/UnitTests/Views/Menuv1/MenuBarv1Tests.cs

@@ -467,7 +467,7 @@ public class MenuBarv1Tests (ITestOutputHelper output)
         Button.DefaultShadow = ShadowStyle.None;
         Button.DefaultShadow = ShadowStyle.None;
 
 
         Toplevel top = new ();
         Toplevel top = new ();
-        var win = new Window ();
+        Window win = new ();
         top.Add (win);
         top.Add (win);
         RunState rsTop = Application.Begin (top);
         RunState rsTop = Application.Begin (top);
         AutoInitShutdownAttribute.FakeResize(new Size(40, 15))    ;
         AutoInitShutdownAttribute.FakeResize(new Size(40, 15))    ;
@@ -503,8 +503,8 @@ public class MenuBarv1Tests (ITestOutputHelper output)
             "Save As",
             "Save As",
             "Delete"
             "Delete"
         };
         };
-        var dialog = new Dialog { X = 2, Y = 2, Width = 15, Height = 4 };
-        var menu = new MenuBar { X = Pos.Center (), Width = 10 };
+        Dialog dialog = new () { X = 2, Y = 2, Width = 15, Height = 4 };
+        MenuBar menu = new () { X = Pos.Center (), Width = 10 };
 
 
         menu.Menus = new MenuBarItem []
         menu.Menus = new MenuBarItem []
         {
         {
@@ -572,7 +572,7 @@ public class MenuBarv1Tests (ITestOutputHelper output)
         }
         }
 
 
         RunState rsDialog = Application.Begin (dialog);
         RunState rsDialog = Application.Begin (dialog);
-        Application.RunIteration (ref rsDialog);
+        AutoInitShutdownAttribute.RunIteration ();
 
 
         Assert.Equal (new (2, 2, 15, 4), dialog.Frame);
         Assert.Equal (new (2, 2, 15, 4), dialog.Frame);
 
 
@@ -598,7 +598,7 @@ public class MenuBarv1Tests (ITestOutputHelper output)
 
 
         Assert.Equal ("File", menu.Menus [0].Title);
         Assert.Equal ("File", menu.Menus [0].Title);
         menu.OpenMenu ();
         menu.OpenMenu ();
-        Application.RunIteration (ref rsDialog);
+        AutoInitShutdownAttribute.RunIteration ();
 
 
         DriverAssert.AssertDriverContentsWithFrameAre (
         DriverAssert.AssertDriverContentsWithFrameAre (
                                                       @"
                                                       @"
@@ -624,8 +624,7 @@ public class MenuBarv1Tests (ITestOutputHelper output)
 
 
         // Need to fool MainLoop into thinking it's running
         // Need to fool MainLoop into thinking it's running
         Application.MainLoop.Running = true;
         Application.MainLoop.Running = true;
-        bool firstIteration = true;
-        Application.RunIteration (ref rsDialog, firstIteration);
+        AutoInitShutdownAttribute.RunIteration ();
         Assert.Equal (items [0], menu.Menus [0].Title);
         Assert.Equal (items [0], menu.Menus [0].Title);
 
 
         DriverAssert.AssertDriverContentsWithFrameAre (
         DriverAssert.AssertDriverContentsWithFrameAre (
@@ -654,13 +653,13 @@ public class MenuBarv1Tests (ITestOutputHelper output)
 
 
             Application.RaiseMouseEvent (new () { ScreenPosition = new (20, 5 + i), Flags = MouseFlags.Button1Clicked });
             Application.RaiseMouseEvent (new () { ScreenPosition = new (20, 5 + i), Flags = MouseFlags.Button1Clicked });
 
 
-            Application.RunIteration (ref rsDialog);
+            AutoInitShutdownAttribute.RunIteration ();
             Assert.Equal (items [i], menu.Menus [0].Title);
             Assert.Equal (items [i], menu.Menus [0].Title);
         }
         }
 
 
         AutoInitShutdownAttribute.FakeResize(new Size(20, 15));
         AutoInitShutdownAttribute.FakeResize(new Size(20, 15));
         menu.OpenMenu ();
         menu.OpenMenu ();
-        Application.RunIteration (ref rsDialog);
+        AutoInitShutdownAttribute.RunIteration ();
 
 
         DriverAssert.AssertDriverContentsWithFrameAre (
         DriverAssert.AssertDriverContentsWithFrameAre (
                                                       @"
                                                       @"
@@ -2578,11 +2577,11 @@ Edit
 
 
             if (i is < 0 or > 0)
             if (i is < 0 or > 0)
             {
             {
-                Assert.Equal (menu, Application.MouseGrabHandler.MouseGrabView);
+                Assert.Equal (menu, Application.Mouse.MouseGrabView);
             }
             }
             else
             else
             {
             {
-                Assert.Equal (menuBar, Application.MouseGrabHandler.MouseGrabView);
+                Assert.Equal (menuBar, Application.Mouse.MouseGrabView);
             }
             }
 
 
             Assert.Equal ("_Edit", miCurrent.Parent.Title);
             Assert.Equal ("_Edit", miCurrent.Parent.Title);

+ 2 - 2
Tests/UnitTests/Views/TextViewTests.cs

@@ -4813,7 +4813,7 @@ This is the second line.
     public void Selected_Text_Shows ()
     public void Selected_Text_Shows ()
     {
     {
         // Proves #3022 is fixed (TextField selected text does not show in v2)
         // Proves #3022 is fixed (TextField selected text does not show in v2)
-        var top = new Toplevel ();
+        Toplevel top = new ();
         top.Add (_textView);
         top.Add (_textView);
         RunState rs = Application.Begin (top);
         RunState rs = Application.Begin (top);
 
 
@@ -4833,7 +4833,7 @@ This is the second line.
 
 
         _textView.NewKeyDownEvent (Key.CursorRight.WithCtrl.WithShift);
         _textView.NewKeyDownEvent (Key.CursorRight.WithCtrl.WithShift);
 
 
-        Application.RunIteration (ref rs, true);
+        AutoInitShutdownAttribute.RunIteration ();
         Assert.Equal (new (4, 0), _textView.CursorPosition);
         Assert.Equal (new (4, 0), _textView.CursorPosition);
 
 
         //                                             TAB to jump between text fields.
         //                                             TAB to jump between text fields.

+ 28 - 28
Tests/UnitTests/Views/ToplevelTests.cs

@@ -305,17 +305,17 @@ public class ToplevelTests
                                      }
                                      }
                                      else if (iterations == 2)
                                      else if (iterations == 2)
                                      {
                                      {
-                                         Assert.Null (Application.MouseGrabHandler.MouseGrabView);
+                                         Assert.Null (Application.Mouse.MouseGrabView);
 
 
                                          // Grab the mouse
                                          // Grab the mouse
                                          Application.RaiseMouseEvent (new () { ScreenPosition = new (3, 2), Flags = MouseFlags.Button1Pressed });
                                          Application.RaiseMouseEvent (new () { ScreenPosition = new (3, 2), Flags = MouseFlags.Button1Pressed });
 
 
-                                         Assert.Equal (Application.Top!.Border, Application.MouseGrabHandler.MouseGrabView);
+                                         Assert.Equal (Application.Top!.Border, Application.Mouse.MouseGrabView);
                                          Assert.Equal (new (2, 2, 10, 3), Application.Top.Frame);
                                          Assert.Equal (new (2, 2, 10, 3), Application.Top.Frame);
                                      }
                                      }
                                      else if (iterations == 3)
                                      else if (iterations == 3)
                                      {
                                      {
-                                         Assert.Equal (Application.Top!.Border, Application.MouseGrabHandler.MouseGrabView);
+                                         Assert.Equal (Application.Top!.Border, Application.Mouse.MouseGrabView);
 
 
                                          // Drag to left
                                          // Drag to left
                                          Application.RaiseMouseEvent (
                                          Application.RaiseMouseEvent (
@@ -326,19 +326,19 @@ public class ToplevelTests
                                                                       });
                                                                       });
                                          AutoInitShutdownAttribute.RunIteration ();
                                          AutoInitShutdownAttribute.RunIteration ();
 
 
-                                         Assert.Equal (Application.Top.Border, Application.MouseGrabHandler.MouseGrabView);
+                                         Assert.Equal (Application.Top.Border, Application.Mouse.MouseGrabView);
                                          Assert.Equal (new (1, 2, 10, 3), Application.Top.Frame);
                                          Assert.Equal (new (1, 2, 10, 3), Application.Top.Frame);
                                      }
                                      }
                                      else if (iterations == 4)
                                      else if (iterations == 4)
                                      {
                                      {
-                                         Assert.Equal (Application.Top!.Border, Application.MouseGrabHandler.MouseGrabView);
+                                         Assert.Equal (Application.Top!.Border, Application.Mouse.MouseGrabView);
                                          Assert.Equal (new (1, 2), Application.Top.Frame.Location);
                                          Assert.Equal (new (1, 2), Application.Top.Frame.Location);
 
 
-                                         Assert.Equal (Application.Top.Border, Application.MouseGrabHandler.MouseGrabView);
+                                         Assert.Equal (Application.Top.Border, Application.Mouse.MouseGrabView);
                                      }
                                      }
                                      else if (iterations == 5)
                                      else if (iterations == 5)
                                      {
                                      {
-                                         Assert.Equal (Application.Top!.Border, Application.MouseGrabHandler.MouseGrabView);
+                                         Assert.Equal (Application.Top!.Border, Application.Mouse.MouseGrabView);
 
 
                                          // Drag up
                                          // Drag up
                                          Application.RaiseMouseEvent (
                                          Application.RaiseMouseEvent (
@@ -349,26 +349,26 @@ public class ToplevelTests
                                                                       });
                                                                       });
                                          AutoInitShutdownAttribute.RunIteration ();
                                          AutoInitShutdownAttribute.RunIteration ();
 
 
-                                         Assert.Equal (Application.Top!.Border, Application.MouseGrabHandler.MouseGrabView);
+                                         Assert.Equal (Application.Top!.Border, Application.Mouse.MouseGrabView);
                                          Assert.Equal (new (1, 1, 10, 3), Application.Top.Frame);
                                          Assert.Equal (new (1, 1, 10, 3), Application.Top.Frame);
                                      }
                                      }
                                      else if (iterations == 6)
                                      else if (iterations == 6)
                                      {
                                      {
-                                         Assert.Equal (Application.Top!.Border, Application.MouseGrabHandler.MouseGrabView);
+                                         Assert.Equal (Application.Top!.Border, Application.Mouse.MouseGrabView);
                                          Assert.Equal (new (1, 1), Application.Top.Frame.Location);
                                          Assert.Equal (new (1, 1), Application.Top.Frame.Location);
 
 
-                                         Assert.Equal (Application.Top.Border, Application.MouseGrabHandler.MouseGrabView);
+                                         Assert.Equal (Application.Top.Border, Application.Mouse.MouseGrabView);
                                          Assert.Equal (new (1, 1, 10, 3), Application.Top.Frame);
                                          Assert.Equal (new (1, 1, 10, 3), Application.Top.Frame);
                                      }
                                      }
                                      else if (iterations == 7)
                                      else if (iterations == 7)
                                      {
                                      {
-                                         Assert.Equal (Application.Top!.Border, Application.MouseGrabHandler.MouseGrabView);
+                                         Assert.Equal (Application.Top!.Border, Application.Mouse.MouseGrabView);
 
 
                                          // Ungrab the mouse
                                          // Ungrab the mouse
                                          Application.RaiseMouseEvent (new () { ScreenPosition = new (2, 1), Flags = MouseFlags.Button1Released });
                                          Application.RaiseMouseEvent (new () { ScreenPosition = new (2, 1), Flags = MouseFlags.Button1Released });
                                          AutoInitShutdownAttribute.RunIteration ();
                                          AutoInitShutdownAttribute.RunIteration ();
 
 
-                                         Assert.Null (Application.MouseGrabHandler.MouseGrabView);
+                                         Assert.Null (Application.Mouse.MouseGrabView);
                                      }
                                      }
                                      else if (iterations == 8)
                                      else if (iterations == 8)
                                      {
                                      {
@@ -411,7 +411,7 @@ public class ToplevelTests
                                      {
                                      {
                                          location = win.Frame;
                                          location = win.Frame;
 
 
-                                         Assert.Null (Application.MouseGrabHandler.MouseGrabView);
+                                         Assert.Null (Application.Mouse.MouseGrabView);
 
 
                                          // Grab the mouse
                                          // Grab the mouse
                                          Application.RaiseMouseEvent (
                                          Application.RaiseMouseEvent (
@@ -420,11 +420,11 @@ public class ToplevelTests
                                                                           ScreenPosition = new (win.Frame.X, win.Frame.Y), Flags = MouseFlags.Button1Pressed
                                                                           ScreenPosition = new (win.Frame.X, win.Frame.Y), Flags = MouseFlags.Button1Pressed
                                                                       });
                                                                       });
 
 
-                                         Assert.Equal (win.Border, Application.MouseGrabHandler.MouseGrabView);
+                                         Assert.Equal (win.Border, Application.Mouse.MouseGrabView);
                                      }
                                      }
                                      else if (iterations == 2)
                                      else if (iterations == 2)
                                      {
                                      {
-                                         Assert.Equal (win.Border, Application.MouseGrabHandler.MouseGrabView);
+                                         Assert.Equal (win.Border, Application.Mouse.MouseGrabView);
 
 
                                          // Drag to left
                                          // Drag to left
                                          movex = 1;
                                          movex = 1;
@@ -438,18 +438,18 @@ public class ToplevelTests
                                                                               | MouseFlags.ReportMousePosition
                                                                               | MouseFlags.ReportMousePosition
                                                                       });
                                                                       });
 
 
-                                         Assert.Equal (win.Border, Application.MouseGrabHandler.MouseGrabView);
+                                         Assert.Equal (win.Border, Application.Mouse.MouseGrabView);
                                      }
                                      }
                                      else if (iterations == 3)
                                      else if (iterations == 3)
                                      {
                                      {
                                          // we should have moved +1, +0
                                          // we should have moved +1, +0
-                                         Assert.Equal (win.Border, Application.MouseGrabHandler.MouseGrabView);
-                                         Assert.Equal (win.Border, Application.MouseGrabHandler.MouseGrabView);
+                                         Assert.Equal (win.Border, Application.Mouse.MouseGrabView);
+                                         Assert.Equal (win.Border, Application.Mouse.MouseGrabView);
                                          location.Offset (movex, movey);
                                          location.Offset (movex, movey);
                                      }
                                      }
                                      else if (iterations == 4)
                                      else if (iterations == 4)
                                      {
                                      {
-                                         Assert.Equal (win.Border, Application.MouseGrabHandler.MouseGrabView);
+                                         Assert.Equal (win.Border, Application.Mouse.MouseGrabView);
 
 
                                          // Drag up
                                          // Drag up
                                          movex = 0;
                                          movex = 0;
@@ -463,18 +463,18 @@ public class ToplevelTests
                                                                               | MouseFlags.ReportMousePosition
                                                                               | MouseFlags.ReportMousePosition
                                                                       });
                                                                       });
 
 
-                                         Assert.Equal (win.Border, Application.MouseGrabHandler.MouseGrabView);
+                                         Assert.Equal (win.Border, Application.Mouse.MouseGrabView);
                                      }
                                      }
                                      else if (iterations == 5)
                                      else if (iterations == 5)
                                      {
                                      {
                                          // we should have moved +0, -1
                                          // we should have moved +0, -1
-                                         Assert.Equal (win.Border, Application.MouseGrabHandler.MouseGrabView);
+                                         Assert.Equal (win.Border, Application.Mouse.MouseGrabView);
                                          location.Offset (movex, movey);
                                          location.Offset (movex, movey);
                                          Assert.Equal (location, win.Frame);
                                          Assert.Equal (location, win.Frame);
                                      }
                                      }
                                      else if (iterations == 6)
                                      else if (iterations == 6)
                                      {
                                      {
-                                         Assert.Equal (win.Border, Application.MouseGrabHandler.MouseGrabView);
+                                         Assert.Equal (win.Border, Application.Mouse.MouseGrabView);
 
 
                                          // Ungrab the mouse
                                          // Ungrab the mouse
                                          movex = 0;
                                          movex = 0;
@@ -487,7 +487,7 @@ public class ToplevelTests
                                                                           Flags = MouseFlags.Button1Released
                                                                           Flags = MouseFlags.Button1Released
                                                                       });
                                                                       });
 
 
-                                         Assert.Null (Application.MouseGrabHandler.MouseGrabView);
+                                         Assert.Null (Application.Mouse.MouseGrabView);
                                      }
                                      }
                                      else if (iterations == 7)
                                      else if (iterations == 7)
                                      {
                                      {
@@ -602,11 +602,11 @@ public class ToplevelTests
         Assert.Equal (new (0, 0, 40, 10), top.Frame);
         Assert.Equal (new (0, 0, 40, 10), top.Frame);
         Assert.Equal (new (0, 0, 20, 3), window.Frame);
         Assert.Equal (new (0, 0, 20, 3), window.Frame);
 
 
-        Assert.Null (Application.MouseGrabHandler.MouseGrabView);
+        Assert.Null (Application.Mouse.MouseGrabView);
 
 
         Application.RaiseMouseEvent (new () { ScreenPosition = new (0, 0), Flags = MouseFlags.Button1Pressed });
         Application.RaiseMouseEvent (new () { ScreenPosition = new (0, 0), Flags = MouseFlags.Button1Pressed });
 
 
-        Assert.Equal (window.Border, Application.MouseGrabHandler.MouseGrabView);
+        Assert.Equal (window.Border, Application.Mouse.MouseGrabView);
 
 
         Application.RaiseMouseEvent (
         Application.RaiseMouseEvent (
                                      new ()
                                      new ()
@@ -694,14 +694,14 @@ public class ToplevelTests
 
 
         RunState rs = Application.Begin (window);
         RunState rs = Application.Begin (window);
 
 
-        Assert.Null (Application.MouseGrabHandler.MouseGrabView);
+        Assert.Null (Application.Mouse.MouseGrabView);
         Assert.Equal (new (0, 0, 10, 3), window.Frame);
         Assert.Equal (new (0, 0, 10, 3), window.Frame);
 
 
         Application.RaiseMouseEvent (new () { ScreenPosition = new (0, 0), Flags = MouseFlags.Button1Pressed });
         Application.RaiseMouseEvent (new () { ScreenPosition = new (0, 0), Flags = MouseFlags.Button1Pressed });
 
 
         var firstIteration = false;
         var firstIteration = false;
         AutoInitShutdownAttribute.RunIteration ();
         AutoInitShutdownAttribute.RunIteration ();
-        Assert.Equal (window.Border, Application.MouseGrabHandler.MouseGrabView);
+        Assert.Equal (window.Border, Application.Mouse.MouseGrabView);
 
 
         Assert.Equal (new (0, 0, 10, 3), window.Frame);
         Assert.Equal (new (0, 0, 10, 3), window.Frame);
 
 
@@ -713,7 +713,7 @@ public class ToplevelTests
 
 
         firstIteration = false;
         firstIteration = false;
         AutoInitShutdownAttribute.RunIteration ();
         AutoInitShutdownAttribute.RunIteration ();
-        Assert.Equal (window.Border, Application.MouseGrabHandler.MouseGrabView);
+        Assert.Equal (window.Border, Application.Mouse.MouseGrabView);
         Assert.Equal (new (1, 1, 10, 3), window.Frame);
         Assert.Equal (new (1, 1, 10, 3), window.Frame);
 
 
         Application.End (rs);
         Application.End (rs);

+ 33 - 33
Tests/UnitTestsParallelizable/Application/KeyboardTests.cs

@@ -13,7 +13,7 @@ public class KeyboardTests
     public void Constructor_InitializesKeyBindings ()
     public void Constructor_InitializesKeyBindings ()
     {
     {
         // Arrange & Act
         // Arrange & Act
-        var keyboard = new Keyboard ();
+        var keyboard = new KeyboardImpl ();
 
 
         // Assert
         // Assert
         Assert.NotNull (keyboard.KeyBindings);
         Assert.NotNull (keyboard.KeyBindings);
@@ -25,7 +25,7 @@ public class KeyboardTests
     public void QuitKey_DefaultValue_IsEsc ()
     public void QuitKey_DefaultValue_IsEsc ()
     {
     {
         // Arrange
         // Arrange
-        var keyboard = new Keyboard ();
+        var keyboard = new KeyboardImpl ();
 
 
         // Assert
         // Assert
         Assert.Equal (Key.Esc, keyboard.QuitKey);
         Assert.Equal (Key.Esc, keyboard.QuitKey);
@@ -35,7 +35,7 @@ public class KeyboardTests
     public void QuitKey_SetValue_UpdatesKeyBindings ()
     public void QuitKey_SetValue_UpdatesKeyBindings ()
     {
     {
         // Arrange
         // Arrange
-        var keyboard = new Keyboard ();
+        var keyboard = new KeyboardImpl ();
         Key newQuitKey = Key.Q.WithCtrl;
         Key newQuitKey = Key.Q.WithCtrl;
 
 
         // Act
         // Act
@@ -51,7 +51,7 @@ public class KeyboardTests
     public void ArrangeKey_DefaultValue_IsCtrlF5 ()
     public void ArrangeKey_DefaultValue_IsCtrlF5 ()
     {
     {
         // Arrange
         // Arrange
-        var keyboard = new Keyboard ();
+        var keyboard = new KeyboardImpl ();
 
 
         // Assert
         // Assert
         Assert.Equal (Key.F5.WithCtrl, keyboard.ArrangeKey);
         Assert.Equal (Key.F5.WithCtrl, keyboard.ArrangeKey);
@@ -61,7 +61,7 @@ public class KeyboardTests
     public void NextTabKey_DefaultValue_IsTab ()
     public void NextTabKey_DefaultValue_IsTab ()
     {
     {
         // Arrange
         // Arrange
-        var keyboard = new Keyboard ();
+        var keyboard = new KeyboardImpl ();
 
 
         // Assert
         // Assert
         Assert.Equal (Key.Tab, keyboard.NextTabKey);
         Assert.Equal (Key.Tab, keyboard.NextTabKey);
@@ -71,7 +71,7 @@ public class KeyboardTests
     public void PrevTabKey_DefaultValue_IsShiftTab ()
     public void PrevTabKey_DefaultValue_IsShiftTab ()
     {
     {
         // Arrange
         // Arrange
-        var keyboard = new Keyboard ();
+        var keyboard = new KeyboardImpl ();
 
 
         // Assert
         // Assert
         Assert.Equal (Key.Tab.WithShift, keyboard.PrevTabKey);
         Assert.Equal (Key.Tab.WithShift, keyboard.PrevTabKey);
@@ -81,7 +81,7 @@ public class KeyboardTests
     public void NextTabGroupKey_DefaultValue_IsF6 ()
     public void NextTabGroupKey_DefaultValue_IsF6 ()
     {
     {
         // Arrange
         // Arrange
-        var keyboard = new Keyboard ();
+        var keyboard = new KeyboardImpl ();
 
 
         // Assert
         // Assert
         Assert.Equal (Key.F6, keyboard.NextTabGroupKey);
         Assert.Equal (Key.F6, keyboard.NextTabGroupKey);
@@ -91,7 +91,7 @@ public class KeyboardTests
     public void PrevTabGroupKey_DefaultValue_IsShiftF6 ()
     public void PrevTabGroupKey_DefaultValue_IsShiftF6 ()
     {
     {
         // Arrange
         // Arrange
-        var keyboard = new Keyboard ();
+        var keyboard = new KeyboardImpl ();
 
 
         // Assert
         // Assert
         Assert.Equal (Key.F6.WithShift, keyboard.PrevTabGroupKey);
         Assert.Equal (Key.F6.WithShift, keyboard.PrevTabGroupKey);
@@ -101,7 +101,7 @@ public class KeyboardTests
     public void KeyBindings_Add_CanAddCustomBinding ()
     public void KeyBindings_Add_CanAddCustomBinding ()
     {
     {
         // Arrange
         // Arrange
-        var keyboard = new Keyboard ();
+        var keyboard = new KeyboardImpl ();
         Key customKey = Key.K.WithCtrl;
         Key customKey = Key.K.WithCtrl;
 
 
         // Act
         // Act
@@ -116,7 +116,7 @@ public class KeyboardTests
     public void KeyBindings_Remove_CanRemoveBinding ()
     public void KeyBindings_Remove_CanRemoveBinding ()
     {
     {
         // Arrange
         // Arrange
-        var keyboard = new Keyboard ();
+        var keyboard = new KeyboardImpl ();
         Key customKey = Key.K.WithCtrl;
         Key customKey = Key.K.WithCtrl;
         keyboard.KeyBindings.Add (customKey, Command.Accept);
         keyboard.KeyBindings.Add (customKey, Command.Accept);
 
 
@@ -131,7 +131,7 @@ public class KeyboardTests
     public void KeyDown_Event_CanBeSubscribed ()
     public void KeyDown_Event_CanBeSubscribed ()
     {
     {
         // Arrange
         // Arrange
-        var keyboard = new Keyboard ();
+        var keyboard = new KeyboardImpl ();
         bool eventRaised = false;
         bool eventRaised = false;
 
 
         // Act
         // Act
@@ -148,7 +148,7 @@ public class KeyboardTests
     public void KeyUp_Event_CanBeSubscribed ()
     public void KeyUp_Event_CanBeSubscribed ()
     {
     {
         // Arrange
         // Arrange
-        var keyboard = new Keyboard ();
+        var keyboard = new KeyboardImpl ();
         bool eventRaised = false;
         bool eventRaised = false;
 
 
         // Act
         // Act
@@ -165,7 +165,7 @@ public class KeyboardTests
     public void InvokeCommand_WithInvalidCommand_ThrowsNotSupportedException ()
     public void InvokeCommand_WithInvalidCommand_ThrowsNotSupportedException ()
     {
     {
         // Arrange
         // Arrange
-        var keyboard = new Keyboard ();
+        var keyboard = new KeyboardImpl ();
         // Pick a command that isn't registered
         // Pick a command that isn't registered
         Command invalidCommand = (Command)9999;
         Command invalidCommand = (Command)9999;
         Key testKey = Key.A;
         Key testKey = Key.A;
@@ -179,8 +179,8 @@ public class KeyboardTests
     public void Multiple_Keyboards_CanExistIndependently ()
     public void Multiple_Keyboards_CanExistIndependently ()
     {
     {
         // Arrange & Act
         // Arrange & Act
-        var keyboard1 = new Keyboard ();
-        var keyboard2 = new Keyboard ();
+        var keyboard1 = new KeyboardImpl ();
+        var keyboard2 = new KeyboardImpl ();
 
 
         keyboard1.QuitKey = Key.Q.WithCtrl;
         keyboard1.QuitKey = Key.Q.WithCtrl;
         keyboard2.QuitKey = Key.X.WithCtrl;
         keyboard2.QuitKey = Key.X.WithCtrl;
@@ -195,7 +195,7 @@ public class KeyboardTests
     public void KeyBindings_Replace_UpdatesExistingBinding ()
     public void KeyBindings_Replace_UpdatesExistingBinding ()
     {
     {
         // Arrange
         // Arrange
-        var keyboard = new Keyboard ();
+        var keyboard = new KeyboardImpl ();
         Key oldKey = Key.Esc;
         Key oldKey = Key.Esc;
         Key newKey = Key.Q.WithCtrl;
         Key newKey = Key.Q.WithCtrl;
 
 
@@ -217,7 +217,7 @@ public class KeyboardTests
     public void KeyBindings_Clear_RemovesAllBindings ()
     public void KeyBindings_Clear_RemovesAllBindings ()
     {
     {
         // Arrange
         // Arrange
-        var keyboard = new Keyboard ();
+        var keyboard = new KeyboardImpl ();
         // Verify initial state has bindings
         // Verify initial state has bindings
         Assert.True (keyboard.KeyBindings.TryGet (keyboard.QuitKey, out _));
         Assert.True (keyboard.KeyBindings.TryGet (keyboard.QuitKey, out _));
 
 
@@ -232,7 +232,7 @@ public class KeyboardTests
     public void AddKeyBindings_PopulatesDefaultBindings ()
     public void AddKeyBindings_PopulatesDefaultBindings ()
     {
     {
         // Arrange
         // Arrange
-        var keyboard = new Keyboard ();
+        var keyboard = new KeyboardImpl ();
         keyboard.KeyBindings.Clear ();
         keyboard.KeyBindings.Clear ();
         Assert.False (keyboard.KeyBindings.TryGet (keyboard.QuitKey, out _));
         Assert.False (keyboard.KeyBindings.TryGet (keyboard.QuitKey, out _));
 
 
@@ -250,7 +250,7 @@ public class KeyboardTests
     public void KeyBindings_Add_Adds ()
     public void KeyBindings_Add_Adds ()
     {
     {
         // Arrange
         // Arrange
-        var keyboard = new Keyboard ();
+        var keyboard = new KeyboardImpl ();
 
 
         // Act
         // Act
         keyboard.KeyBindings.Add (Key.A, Command.Accept);
         keyboard.KeyBindings.Add (Key.A, Command.Accept);
@@ -267,7 +267,7 @@ public class KeyboardTests
     public void KeyBindings_Remove_Removes ()
     public void KeyBindings_Remove_Removes ()
     {
     {
         // Arrange
         // Arrange
-        var keyboard = new Keyboard ();
+        var keyboard = new KeyboardImpl ();
         keyboard.KeyBindings.Add (Key.A, Command.Accept);
         keyboard.KeyBindings.Add (Key.A, Command.Accept);
         Assert.True (keyboard.KeyBindings.TryGet (Key.A, out _));
         Assert.True (keyboard.KeyBindings.TryGet (Key.A, out _));
 
 
@@ -282,7 +282,7 @@ public class KeyboardTests
     public void QuitKey_Default_Is_Esc ()
     public void QuitKey_Default_Is_Esc ()
     {
     {
         // Arrange & Act
         // Arrange & Act
-        var keyboard = new Keyboard ();
+        var keyboard = new KeyboardImpl ();
 
 
         // Assert
         // Assert
         Assert.Equal (Key.Esc, keyboard.QuitKey);
         Assert.Equal (Key.Esc, keyboard.QuitKey);
@@ -292,7 +292,7 @@ public class KeyboardTests
     public void QuitKey_Setter_UpdatesBindings ()
     public void QuitKey_Setter_UpdatesBindings ()
     {
     {
         // Arrange
         // Arrange
-        var keyboard = new Keyboard ();
+        var keyboard = new KeyboardImpl ();
         Key prevKey = keyboard.QuitKey;
         Key prevKey = keyboard.QuitKey;
 
 
         // Act - Change QuitKey
         // Act - Change QuitKey
@@ -309,7 +309,7 @@ public class KeyboardTests
     public void NextTabKey_Setter_UpdatesBindings ()
     public void NextTabKey_Setter_UpdatesBindings ()
     {
     {
         // Arrange
         // Arrange
-        var keyboard = new Keyboard ();
+        var keyboard = new KeyboardImpl ();
         Key prevKey = keyboard.NextTabKey;
         Key prevKey = keyboard.NextTabKey;
         Key newKey = Key.N.WithCtrl;
         Key newKey = Key.N.WithCtrl;
 
 
@@ -326,7 +326,7 @@ public class KeyboardTests
     public void PrevTabKey_Setter_UpdatesBindings ()
     public void PrevTabKey_Setter_UpdatesBindings ()
     {
     {
         // Arrange
         // Arrange
-        var keyboard = new Keyboard ();
+        var keyboard = new KeyboardImpl ();
         Key newKey = Key.P.WithCtrl;
         Key newKey = Key.P.WithCtrl;
 
 
         // Act
         // Act
@@ -342,7 +342,7 @@ public class KeyboardTests
     public void NextTabGroupKey_Setter_UpdatesBindings ()
     public void NextTabGroupKey_Setter_UpdatesBindings ()
     {
     {
         // Arrange
         // Arrange
-        var keyboard = new Keyboard ();
+        var keyboard = new KeyboardImpl ();
         Key newKey = Key.PageDown.WithCtrl;
         Key newKey = Key.PageDown.WithCtrl;
 
 
         // Act
         // Act
@@ -359,7 +359,7 @@ public class KeyboardTests
     public void PrevTabGroupKey_Setter_UpdatesBindings ()
     public void PrevTabGroupKey_Setter_UpdatesBindings ()
     {
     {
         // Arrange
         // Arrange
-        var keyboard = new Keyboard ();
+        var keyboard = new KeyboardImpl ();
         Key newKey = Key.PageUp.WithCtrl;
         Key newKey = Key.PageUp.WithCtrl;
 
 
         // Act
         // Act
@@ -376,7 +376,7 @@ public class KeyboardTests
     public void ArrangeKey_Setter_UpdatesBindings ()
     public void ArrangeKey_Setter_UpdatesBindings ()
     {
     {
         // Arrange
         // Arrange
-        var keyboard = new Keyboard ();
+        var keyboard = new KeyboardImpl ();
         Key newKey = Key.A.WithCtrl;
         Key newKey = Key.A.WithCtrl;
 
 
         // Act
         // Act
@@ -392,7 +392,7 @@ public class KeyboardTests
     public void KeyBindings_AddWithTarget_StoresTarget ()
     public void KeyBindings_AddWithTarget_StoresTarget ()
     {
     {
         // Arrange
         // Arrange
-        var keyboard = new Keyboard ();
+        var keyboard = new KeyboardImpl ();
         var view = new View ();
         var view = new View ();
 
 
         // Act
         // Act
@@ -410,7 +410,7 @@ public class KeyboardTests
     public void InvokeCommandsBoundToKey_ReturnsNull_WhenNoBindingExists ()
     public void InvokeCommandsBoundToKey_ReturnsNull_WhenNoBindingExists ()
     {
     {
         // Arrange
         // Arrange
-        var keyboard = new Keyboard ();
+        var keyboard = new KeyboardImpl ();
         Key unboundKey = Key.Z.WithAlt.WithCtrl;
         Key unboundKey = Key.Z.WithAlt.WithCtrl;
 
 
         // Act
         // Act
@@ -424,7 +424,7 @@ public class KeyboardTests
     public void InvokeCommandsBoundToKey_InvokesCommand_WhenBindingExists ()
     public void InvokeCommandsBoundToKey_InvokesCommand_WhenBindingExists ()
     {
     {
         // Arrange
         // Arrange
-        var keyboard = new Keyboard ();
+        var keyboard = new KeyboardImpl ();
         // QuitKey has a bound command by default
         // QuitKey has a bound command by default
 
 
         // Act
         // Act
@@ -440,8 +440,8 @@ public class KeyboardTests
     public void Multiple_Keyboards_Independent_KeyBindings ()
     public void Multiple_Keyboards_Independent_KeyBindings ()
     {
     {
         // Arrange
         // Arrange
-        var keyboard1 = new Keyboard ();
-        var keyboard2 = new Keyboard ();
+        var keyboard1 = new KeyboardImpl ();
+        var keyboard2 = new KeyboardImpl ();
 
 
         // Act
         // Act
         keyboard1.KeyBindings.Add (Key.X, Command.Accept);
         keyboard1.KeyBindings.Add (Key.X, Command.Accept);
@@ -459,7 +459,7 @@ public class KeyboardTests
     public void KeyBindings_Replace_PreservesCommandsForNewKey ()
     public void KeyBindings_Replace_PreservesCommandsForNewKey ()
     {
     {
         // Arrange
         // Arrange
-        var keyboard = new Keyboard ();
+        var keyboard = new KeyboardImpl ();
         Key oldKey = Key.Esc;
         Key oldKey = Key.Esc;
         Key newKey = Key.Q.WithCtrl;
         Key newKey = Key.Q.WithCtrl;
 
 

+ 444 - 0
Tests/UnitTestsParallelizable/Application/MouseInterfaceTests.cs

@@ -0,0 +1,444 @@
+using Terminal.Gui.App;
+using Xunit.Abstractions;
+
+namespace UnitTests_Parallelizable.ApplicationTests;
+
+/// <summary>
+///     Parallelizable tests for IMouse interface.
+///     Tests the decoupled mouse handling without Application.Init or global state.
+/// </summary>
+[Trait ("Category", "Input")]
+public class MouseInterfaceTests (ITestOutputHelper output)
+{
+    private readonly ITestOutputHelper _output = output;
+
+    #region IMouse Basic Properties
+
+    [Fact]
+    public void Mouse_LastMousePosition_InitiallyNull ()
+    {
+        // Arrange
+        MouseImpl mouse = new ();
+
+        // Act & Assert
+        Assert.Null (mouse.LastMousePosition);
+    }
+
+    [Theory]
+    [InlineData (0, 0)]
+    [InlineData (10, 20)]
+    [InlineData (-5, -10)]
+    [InlineData (100, 200)]
+    public void Mouse_LastMousePosition_CanBeSetAndRetrieved (int x, int y)
+    {
+        // Arrange
+        MouseImpl mouse = new ();
+        Point testPosition = new (x, y);
+
+        // Act
+        mouse.LastMousePosition = testPosition;
+
+        // Assert
+        Assert.Equal (testPosition, mouse.LastMousePosition);
+        Assert.Equal (testPosition, mouse.GetLastMousePosition ());
+    }
+
+    [Fact]
+    public void Mouse_IsMouseDisabled_DefaultsFalse ()
+    {
+        // Arrange
+        MouseImpl mouse = new ();
+
+        // Act & Assert
+        Assert.False (mouse.IsMouseDisabled);
+    }
+
+    [Theory]
+    [InlineData (true)]
+    [InlineData (false)]
+    public void Mouse_IsMouseDisabled_CanBeSetAndRetrieved (bool disabled)
+    {
+        // Arrange
+        MouseImpl mouse = new ();
+
+        // Act
+        mouse.IsMouseDisabled = disabled;
+
+        // Assert
+        Assert.Equal (disabled, mouse.IsMouseDisabled);
+    }
+
+    [Fact]
+    public void Mouse_CachedViewsUnderMouse_InitiallyEmpty ()
+    {
+        // Arrange
+        MouseImpl mouse = new ();
+
+        // Act & Assert
+        Assert.NotNull (mouse.CachedViewsUnderMouse);
+        Assert.Empty (mouse.CachedViewsUnderMouse);
+    }
+
+    #endregion
+
+    #region IMouse Event Handling
+
+    [Fact]
+    public void Mouse_MouseEvent_CanSubscribeAndFire ()
+    {
+        // Arrange
+        MouseImpl mouse = new ();
+        var eventFired = false;
+        MouseEventArgs capturedArgs = null;
+
+        mouse.MouseEvent += (sender, args) =>
+        {
+            eventFired = true;
+            capturedArgs = args;
+        };
+
+        MouseEventArgs testEvent = new ()
+        {
+            ScreenPosition = new Point (5, 10),
+            Flags = MouseFlags.Button1Pressed
+        };
+
+        // Act
+        mouse.RaiseMouseEvent (testEvent);
+
+        // Assert
+        Assert.True (eventFired);
+        Assert.NotNull (capturedArgs);
+        Assert.Equal (testEvent.ScreenPosition, capturedArgs.ScreenPosition);
+        Assert.Equal (testEvent.Flags, capturedArgs.Flags);
+    }
+
+    [Fact]
+    public void Mouse_MouseEvent_CanUnsubscribe ()
+    {
+        // Arrange
+        MouseImpl mouse = new ();
+        var eventCount = 0;
+
+        void Handler (object sender, MouseEventArgs args) => eventCount++;
+
+        mouse.MouseEvent += Handler;
+
+        MouseEventArgs testEvent = new ()
+        {
+            ScreenPosition = new Point (0, 0),
+            Flags = MouseFlags.Button1Pressed
+        };
+
+        // Act - Fire once
+        mouse.RaiseMouseEvent (testEvent);
+        Assert.Equal (1, eventCount);
+
+        // Unsubscribe
+        mouse.MouseEvent -= Handler;
+
+        // Fire again
+        mouse.RaiseMouseEvent (testEvent);
+
+        // Assert - Count should not increase
+        Assert.Equal (1, eventCount);
+    }
+
+    [Fact]
+    public void Mouse_RaiseMouseEvent_WithDisabledMouse_DoesNotFireEvent ()
+    {
+        // Arrange
+        MouseImpl mouse = new ();
+        var eventFired = false;
+
+        mouse.MouseEvent += (sender, args) => { eventFired = true; };
+        mouse.IsMouseDisabled = true;
+
+        MouseEventArgs testEvent = new ()
+        {
+            ScreenPosition = new Point (0, 0),
+            Flags = MouseFlags.Button1Pressed
+        };
+
+        // Act
+        mouse.RaiseMouseEvent (testEvent);
+
+        // Assert
+        Assert.False (eventFired);
+    }
+
+    [Theory]
+    [InlineData (MouseFlags.Button1Pressed)]
+    [InlineData (MouseFlags.Button1Released)]
+    [InlineData (MouseFlags.Button1Clicked)]
+    [InlineData (MouseFlags.Button2Pressed)]
+    [InlineData (MouseFlags.WheeledUp)]
+    [InlineData (MouseFlags.ReportMousePosition)]
+    public void Mouse_RaiseMouseEvent_CorrectlyPassesFlags (MouseFlags flags)
+    {
+        // Arrange
+        MouseImpl mouse = new ();
+        MouseFlags? capturedFlags = null;
+
+        mouse.MouseEvent += (sender, args) => { capturedFlags = args.Flags; };
+
+        MouseEventArgs testEvent = new ()
+        {
+            ScreenPosition = new Point (5, 5),
+            Flags = flags
+        };
+
+        // Act
+        mouse.RaiseMouseEvent (testEvent);
+
+        // Assert
+        Assert.NotNull (capturedFlags);
+        Assert.Equal (flags, capturedFlags.Value);
+    }
+
+    #endregion
+
+    #region IMouse ResetState
+
+    [Fact]
+    public void Mouse_ResetState_ClearsCachedViews ()
+    {
+        // Arrange
+        MouseImpl mouse = new ();
+        View testView = new () { Width = 10, Height = 10 };
+
+        mouse.CachedViewsUnderMouse.Add (testView);
+        Assert.Single (mouse.CachedViewsUnderMouse);
+
+        // Act
+        mouse.ResetState ();
+
+        // Assert
+        Assert.Empty (mouse.CachedViewsUnderMouse);
+
+        testView.Dispose ();
+    }
+
+    [Fact]
+    public void Mouse_ResetState_ClearsEventHandlers ()
+    {
+        // Arrange
+        MouseImpl mouse = new ();
+        var eventCount = 0;
+
+        mouse.MouseEvent += (sender, args) => eventCount++;
+
+        MouseEventArgs testEvent = new ()
+        {
+            ScreenPosition = new Point (0, 0),
+            Flags = MouseFlags.Button1Pressed
+        };
+
+        // Verify event fires before reset
+        mouse.RaiseMouseEvent (testEvent);
+        Assert.Equal (1, eventCount);
+
+        // Act
+        mouse.ResetState ();
+
+        // Raise event again
+        mouse.RaiseMouseEvent (testEvent);
+
+        // Assert - Event count should not increase after reset
+        Assert.Equal (1, eventCount);
+    }
+
+    [Fact]
+    public void Mouse_ResetState_DoesNotClearLastMousePosition ()
+    {
+        // Arrange
+        MouseImpl mouse = new ();
+        Point testPosition = new (42, 84);
+
+        mouse.LastMousePosition = testPosition;
+
+        // Act
+        mouse.ResetState ();
+
+        // Assert - LastMousePosition should NOT be cleared (per design)
+        Assert.Equal (testPosition, mouse.LastMousePosition);
+    }
+
+    #endregion
+
+    #region IMouse Isolation
+
+    [Fact]
+    public void Mouse_Instances_AreIndependent ()
+    {
+        // Arrange
+        MouseImpl mouse1 = new ();
+        MouseImpl mouse2 = new ();
+
+        // Act
+        mouse1.IsMouseDisabled = true;
+        mouse1.LastMousePosition = new Point (10, 10);
+
+        // Assert - mouse2 should be unaffected
+        Assert.False (mouse2.IsMouseDisabled);
+        Assert.Null (mouse2.LastMousePosition);
+    }
+
+    [Fact]
+    public void Mouse_Events_AreIndependent ()
+    {
+        // Arrange
+        MouseImpl mouse1 = new ();
+        var mouse1EventCount = 0;
+
+        MouseImpl mouse2 = new ();
+        var mouse2EventCount = 0;
+
+        mouse1.MouseEvent += (sender, args) => mouse1EventCount++;
+        mouse2.MouseEvent += (sender, args) => mouse2EventCount++;
+
+        MouseEventArgs testEvent = new ()
+        {
+            ScreenPosition = new Point (0, 0),
+            Flags = MouseFlags.Button1Pressed
+        };
+
+        // Act
+        mouse1.RaiseMouseEvent (testEvent);
+
+        // Assert
+        Assert.Equal (1, mouse1EventCount);
+        Assert.Equal (0, mouse2EventCount);
+    }
+
+    [Fact]
+    public void Mouse_CachedViews_AreIndependent ()
+    {
+        // Arrange
+        MouseImpl mouse1 = new ();
+        MouseImpl mouse2 = new ();
+
+        View view1 = new ();
+        View view2 = new ();
+
+        // Act
+        mouse1.CachedViewsUnderMouse.Add (view1);
+        mouse2.CachedViewsUnderMouse.Add (view2);
+
+        // Assert
+        Assert.Single (mouse1.CachedViewsUnderMouse);
+        Assert.Single (mouse2.CachedViewsUnderMouse);
+        Assert.Contains (view1, mouse1.CachedViewsUnderMouse);
+        Assert.Contains (view2, mouse2.CachedViewsUnderMouse);
+        Assert.DoesNotContain (view2, mouse1.CachedViewsUnderMouse);
+        Assert.DoesNotContain (view1, mouse2.CachedViewsUnderMouse);
+
+        view1.Dispose ();
+        view2.Dispose ();
+    }
+
+    #endregion
+
+    #region Mouse Grab Tests
+
+    [Fact]
+    public void Mouse_GrabMouse_SetsMouseGrabView ()
+    {
+        // Arrange
+        MouseImpl mouse = new ();
+        View testView = new ();
+
+        // Act
+        mouse.GrabMouse (testView);
+
+        // Assert
+        Assert.Equal (testView, mouse.MouseGrabView);
+    }
+
+    [Fact]
+    public void Mouse_UngrabMouse_ClearsMouseGrabView ()
+    {
+        // Arrange
+        MouseImpl mouse = new ();
+        View testView = new ();
+        mouse.GrabMouse (testView);
+
+        // Act
+        mouse.UngrabMouse ();
+
+        // Assert
+        Assert.Null (mouse.MouseGrabView);
+    }
+
+    [Fact]
+    public void Mouse_GrabbingMouse_CanBeCanceled ()
+    {
+        // Arrange
+        MouseImpl mouse = new ();
+        View testView = new ();
+        var eventFired = false;
+
+        mouse.GrabbingMouse += (sender, args) =>
+        {
+            eventFired = true;
+            args.Cancel = true;
+        };
+
+        // Act
+        mouse.GrabMouse (testView);
+
+        // Assert
+        Assert.True (eventFired);
+        Assert.Null (mouse.MouseGrabView); // Should not be set because it was cancelled
+    }
+
+    [Fact]
+    public void Mouse_GrabbedMouse_EventFired ()
+    {
+        // Arrange
+        MouseImpl mouse = new ();
+        View testView = new ();
+        var eventFired = false;
+        View? eventView = null;
+
+        mouse.GrabbedMouse += (sender, args) =>
+        {
+            eventFired = true;
+            eventView = args.View;
+        };
+
+        // Act
+        mouse.GrabMouse (testView);
+
+        // Assert
+        Assert.True (eventFired);
+        Assert.Equal (testView, eventView);
+    }
+
+    [Fact]
+    public void Mouse_UnGrabbedMouse_EventFired ()
+    {
+        // Arrange
+        MouseImpl mouse = new ();
+        View testView = new ();
+        mouse.GrabMouse (testView);
+
+        var eventFired = false;
+        View? eventView = null;
+
+        mouse.UnGrabbedMouse += (sender, args) =>
+        {
+            eventFired = true;
+            eventView = args.View;
+        };
+
+        // Act
+        mouse.UngrabMouse ();
+
+        // Assert
+        Assert.True (eventFired);
+        Assert.Equal (testView, eventView);
+    }
+
+    #endregion
+}

+ 125 - 0
Tests/UnitTestsParallelizable/Application/MouseTests.cs

@@ -0,0 +1,125 @@
+using Terminal.Gui.App;
+using Xunit.Abstractions;
+
+namespace UnitTests_Parallelizable.ApplicationTests;
+
+/// <summary>
+///     Tests for the <see cref="IMouse"/> interface and <see cref="MouseImpl"/> implementation.
+///     These tests demonstrate the decoupled mouse handling that enables parallel test execution.
+/// </summary>
+public class MouseTests (ITestOutputHelper output)
+{
+    private readonly ITestOutputHelper _output = output;
+
+    [Fact]
+    public void Mouse_Instance_CreatedSuccessfully ()
+    {
+        // Arrange & Act
+        MouseImpl mouse = new ();
+
+        // Assert
+        Assert.NotNull (mouse);
+        Assert.False (mouse.IsMouseDisabled);
+        Assert.Null (mouse.LastMousePosition);
+    }
+
+    [Fact]
+    public void Mouse_LastMousePosition_CanBeSetAndRetrieved ()
+    {
+        // Arrange
+        MouseImpl mouse = new ();
+        Point expectedPosition = new (10, 20);
+
+        // Act
+        mouse.LastMousePosition = expectedPosition;
+        Point? actualPosition = mouse.GetLastMousePosition ();
+
+        // Assert
+        Assert.Equal (expectedPosition, actualPosition);
+    }
+
+    [Fact]
+    public void Mouse_IsMouseDisabled_CanBeSetAndRetrieved ()
+    {
+        // Arrange
+        MouseImpl mouse = new ();
+
+        // Act
+        mouse.IsMouseDisabled = true;
+
+        // Assert
+        Assert.True (mouse.IsMouseDisabled);
+    }
+
+    [Fact]
+    public void Mouse_CachedViewsUnderMouse_InitializedEmpty ()
+    {
+        // Arrange
+        MouseImpl mouse = new ();
+
+        // Assert
+        Assert.NotNull (mouse.CachedViewsUnderMouse);
+        Assert.Empty (mouse.CachedViewsUnderMouse);
+    }
+
+    [Fact]
+    public void Mouse_ResetState_ClearsEventAndCachedViews ()
+    {
+        // Arrange
+        MouseImpl mouse = new ();
+        var eventFired = false;
+        mouse.MouseEvent += (sender, args) => eventFired = true;
+        mouse.CachedViewsUnderMouse.Add (new View ());
+
+        // Act
+        mouse.ResetState ();
+
+        // Assert - CachedViewsUnderMouse should be cleared
+        Assert.Empty (mouse.CachedViewsUnderMouse);
+        
+        // Event handlers should be cleared
+        MouseEventArgs mouseEvent = new () { ScreenPosition = new Point (0, 0), Flags = MouseFlags.Button1Pressed };
+        mouse.RaiseMouseEvent (mouseEvent);
+        Assert.False (eventFired, "Event should not fire after ResetState");
+    }
+
+    [Fact]
+    public void Mouse_RaiseMouseEvent_DoesNotUpdateLastPositionWhenNotInitialized ()
+    {
+        // Arrange
+        MouseImpl mouse = new ();
+        MouseEventArgs mouseEvent = new () { ScreenPosition = new Point (5, 10), Flags = MouseFlags.Button1Pressed };
+
+        // Act - Application is not initialized, so LastMousePosition should not be set
+        mouse.RaiseMouseEvent (mouseEvent);
+
+        // Assert
+        // Since Application.Initialized is false, LastMousePosition should remain null
+        // This behavior matches the original implementation
+        Assert.Null (mouse.LastMousePosition);
+    }
+
+    [Fact]
+    public void Mouse_MouseEvent_CanBeSubscribedAndUnsubscribed ()
+    {
+        // Arrange
+        MouseImpl mouse = new ();
+        var eventCount = 0;
+        EventHandler<MouseEventArgs> handler = (sender, args) => eventCount++;
+
+        // Act - Subscribe
+        mouse.MouseEvent += handler;
+        MouseEventArgs mouseEvent = new () { ScreenPosition = new Point (0, 0), Flags = MouseFlags.Button1Pressed };
+        mouse.RaiseMouseEvent (mouseEvent);
+
+        // Assert - Event fired once
+        Assert.Equal (1, eventCount);
+
+        // Act - Unsubscribe
+        mouse.MouseEvent -= handler;
+        mouse.RaiseMouseEvent (mouseEvent);
+
+        // Assert - Event count unchanged
+        Assert.Equal (1, eventCount);
+    }
+}

+ 1 - 1
Tests/UnitTestsParallelizable/TestSetup.cs

@@ -40,7 +40,7 @@ public class GlobalTestSetup : IDisposable
 
 
         // Public Properties
         // Public Properties
         Assert.Null (Application.Top);
         Assert.Null (Application.Top);
-        Assert.Null (Application.MouseGrabHandler.MouseGrabView);
+        Assert.Null (Application.Mouse.MouseGrabView);
 
 
         // Don't check Application.ForceDriver
         // Don't check Application.ForceDriver
         // Assert.Empty (Application.ForceDriver);
         // Assert.Empty (Application.ForceDriver);

+ 498 - 0
Tests/UnitTestsParallelizable/View/Mouse/MouseEventRoutingTests.cs

@@ -0,0 +1,498 @@
+using Terminal.Gui.App;
+using Xunit.Abstractions;
+
+namespace UnitTests_Parallelizable.ApplicationTests;
+
+/// <summary>
+///     Parallelizable tests for mouse event routing and coordinate transformation.
+///     These tests validate mouse event handling without Application.Begin or global state.
+/// </summary>
+[Trait ("Category", "Input")]
+public class MouseEventRoutingTests (ITestOutputHelper output)
+{
+    private readonly ITestOutputHelper _output = output;
+
+    #region Mouse Event Routing to Views
+
+    [Theory]
+    [InlineData (5, 5, 5, 5, true)]   // Click inside view
+    [InlineData (0, 0, 0, 0, true)]   // Click at origin
+    [InlineData (9, 9, 9, 9, true)]   // Click at far corner (view is 10x10)
+    [InlineData (10, 10, -1, -1, false)] // Click outside view
+    [InlineData (-1, -1, -1, -1, false)] // Click outside view
+    public void View_NewMouseEvent_ReceivesCorrectCoordinates (int screenX, int screenY, int expectedViewX, int expectedViewY, bool shouldReceive)
+    {
+        // Arrange
+        View view = new ()
+        {
+            X = 0,
+            Y = 0,
+            Width = 10,
+            Height = 10
+        };
+
+        Point? receivedPosition = null;
+        var eventReceived = false;
+
+        view.MouseEvent += (sender, args) =>
+        {
+            eventReceived = true;
+            receivedPosition = args.Position;
+        };
+
+        MouseEventArgs mouseEvent = new ()
+        {
+            Position = new Point (screenX, screenY),
+            Flags = MouseFlags.Button1Clicked
+        };
+
+        // Act
+        view.NewMouseEvent (mouseEvent);
+
+        // Assert
+        if (shouldReceive)
+        {
+            Assert.True (eventReceived);
+            Assert.NotNull (receivedPosition);
+            Assert.Equal (expectedViewX, receivedPosition.Value.X);
+            Assert.Equal (expectedViewY, receivedPosition.Value.Y);
+        }
+
+        view.Dispose ();
+    }
+
+    [Theory]
+    [InlineData (0, 0, 5, 5, 5, 5, true)]   // View at origin, click at (5,5) in view
+    [InlineData (10, 10, 5, 5, 5, 5, true)] // View offset, but we still pass view-relative coords
+    [InlineData (0, 0, 0, 0, 0, 0, true)]   // View at origin, click at origin
+    [InlineData (5, 5, 9, 9, 9, 9, true)]   // View offset, click at far corner (view-relative)
+    [InlineData (0, 0, 10, 10, -1, -1, false)] // Click outside view bounds
+    [InlineData (0, 0, -1, -1, -1, -1, false)] // Click outside view bounds
+    public void View_WithOffset_ReceivesCorrectCoordinates (
+        int viewX,
+        int viewY,
+        int viewRelativeX,
+        int viewRelativeY,
+        int expectedViewX,
+        int expectedViewY,
+        bool shouldReceive)
+    {
+        // Arrange
+        // Note: When testing View.NewMouseEvent directly (without Application routing),
+        // coordinates are already view-relative. The view's X/Y position doesn't affect
+        // the coordinate transformation at this level.
+        View view = new ()
+        {
+            X = viewX,
+            Y = viewY,
+            Width = 10,
+            Height = 10
+        };
+
+        Point? receivedPosition = null;
+        var eventReceived = false;
+
+        view.MouseEvent += (sender, args) =>
+        {
+            eventReceived = true;
+            receivedPosition = args.Position;
+        };
+
+        MouseEventArgs mouseEvent = new ()
+        {
+            Position = new Point (viewRelativeX, viewRelativeY),
+            Flags = MouseFlags.Button1Clicked
+        };
+
+        // Act
+        view.NewMouseEvent (mouseEvent);
+
+        // Assert
+        if (shouldReceive)
+        {
+            Assert.True (eventReceived, $"Event should be received at view-relative ({viewRelativeX},{viewRelativeY})");
+            Assert.NotNull (receivedPosition);
+            Assert.Equal (expectedViewX, receivedPosition.Value.X);
+            Assert.Equal (expectedViewY, receivedPosition.Value.Y);
+        }
+
+        view.Dispose ();
+    }
+
+    #endregion
+
+    #region View Hierarchy Mouse Event Routing
+
+    [Fact]
+    public void SubView_ReceivesMouseEvent_WithCorrectRelativeCoordinates ()
+    {
+        // Arrange
+        View superView = new ()
+        {
+            X = 0,
+            Y = 0,
+            Width = 20,
+            Height = 20
+        };
+
+        View subView = new ()
+        {
+            X = 5,
+            Y = 5,
+            Width = 10,
+            Height = 10
+        };
+
+        superView.Add (subView);
+
+        Point? subViewReceivedPosition = null;
+        var subViewEventReceived = false;
+
+        subView.MouseEvent += (sender, args) =>
+        {
+            subViewEventReceived = true;
+            subViewReceivedPosition = args.Position;
+        };
+
+        // Click at position (2, 2) relative to subView (which is at 5,5 relative to superView)
+        MouseEventArgs mouseEvent = new ()
+        {
+            Position = new Point (2, 2), // Relative to subView
+            Flags = MouseFlags.Button1Clicked
+        };
+
+        // Act
+        subView.NewMouseEvent (mouseEvent);
+
+        // Assert
+        Assert.True (subViewEventReceived);
+        Assert.NotNull (subViewReceivedPosition);
+        Assert.Equal (2, subViewReceivedPosition.Value.X);
+        Assert.Equal (2, subViewReceivedPosition.Value.Y);
+
+        subView.Dispose ();
+        superView.Dispose ();
+    }
+
+    [Fact]
+    public void MouseClick_OnSubView_RaisesMouseClickEvent ()
+    {
+        // Arrange
+        View superView = new ()
+        {
+            Width = 20,
+            Height = 20
+        };
+
+        View subView = new ()
+        {
+            X = 5,
+            Y = 5,
+            Width = 10,
+            Height = 10
+        };
+
+        superView.Add (subView);
+
+        var clickCount = 0;
+        subView.MouseClick += (sender, args) => clickCount++;
+
+        MouseEventArgs mouseEvent = new ()
+        {
+            Position = new Point (5, 5),
+            Flags = MouseFlags.Button1Clicked
+        };
+
+        // Act
+        subView.NewMouseEvent (mouseEvent);
+
+        // Assert
+        Assert.Equal (1, clickCount);
+
+        subView.Dispose ();
+        superView.Dispose ();
+    }
+
+    #endregion
+
+    #region Mouse Event Propagation
+
+    [Fact]
+    public void View_HandledEvent_StopsPropagation ()
+    {
+        // Arrange
+        View view = new () { Width = 10, Height = 10 };
+        var handlerCalled = false;
+        var clickHandlerCalled = false;
+
+        view.MouseEvent += (sender, args) =>
+        {
+            handlerCalled = true;
+            args.Handled = true; // Mark as handled
+        };
+
+        view.MouseClick += (sender, args) => { clickHandlerCalled = true; };
+
+        MouseEventArgs mouseEvent = new ()
+        {
+            Position = new Point (5, 5),
+            Flags = MouseFlags.Button1Clicked
+        };
+
+        // Act
+        bool? result = view.NewMouseEvent (mouseEvent);
+
+        // Assert
+        Assert.True (result.HasValue && result.Value); // Event was handled
+        Assert.True (handlerCalled);
+        Assert.False (clickHandlerCalled); // Click handler should not be called when event is handled
+
+        view.Dispose ();
+    }
+
+    [Fact]
+    public void View_UnhandledEvent_ContinuesProcessing ()
+    {
+        // Arrange
+        View view = new () { Width = 10, Height = 10 };
+        var eventHandlerCalled = false;
+        var clickHandlerCalled = false;
+
+        view.MouseEvent += (sender, args) =>
+        {
+            eventHandlerCalled = true;
+            // Don't set Handled = true
+        };
+
+        view.MouseClick += (sender, args) => { clickHandlerCalled = true; };
+
+        MouseEventArgs mouseEvent = new ()
+        {
+            Position = new Point (5, 5),
+            Flags = MouseFlags.Button1Clicked
+        };
+
+        // Act
+        view.NewMouseEvent (mouseEvent);
+
+        // Assert
+        Assert.True (eventHandlerCalled);
+        Assert.True (clickHandlerCalled); // Click handler should be called when event is not handled
+
+        view.Dispose ();
+    }
+
+    #endregion
+
+    #region Mouse Button Events
+
+    [Theory]
+    [InlineData (MouseFlags.Button1Pressed, 1, 0, 0)]
+    [InlineData (MouseFlags.Button1Released, 0, 1, 0)]
+    [InlineData (MouseFlags.Button1Clicked, 0, 0, 1)]
+    public void View_MouseButtonEvents_RaiseCorrectHandlers (MouseFlags flags, int expectedPressed, int expectedReleased, int expectedClicked)
+    {
+        // Arrange
+        View view = new () { Width = 10, Height = 10 };
+        var pressedCount = 0;
+        var releasedCount = 0;
+        var clickedCount = 0;
+
+        view.MouseEvent += (sender, args) =>
+        {
+            if (args.Flags.HasFlag (MouseFlags.Button1Pressed))
+            {
+                pressedCount++;
+            }
+
+            if (args.Flags.HasFlag (MouseFlags.Button1Released))
+            {
+                releasedCount++;
+            }
+        };
+
+        view.MouseClick += (sender, args) => { clickedCount++; };
+
+        MouseEventArgs mouseEvent = new ()
+        {
+            Position = new Point (5, 5),
+            Flags = flags
+        };
+
+        // Act
+        view.NewMouseEvent (mouseEvent);
+
+        // Assert
+        Assert.Equal (expectedPressed, pressedCount);
+        Assert.Equal (expectedReleased, releasedCount);
+        Assert.Equal (expectedClicked, clickedCount);
+
+        view.Dispose ();
+    }
+
+    [Theory]
+    [InlineData (MouseFlags.Button1Clicked)]
+    [InlineData (MouseFlags.Button2Clicked)]
+    [InlineData (MouseFlags.Button3Clicked)]
+    [InlineData (MouseFlags.Button4Clicked)]
+    public void View_AllMouseButtons_TriggerClickEvent (MouseFlags clickFlag)
+    {
+        // Arrange
+        View view = new () { Width = 10, Height = 10 };
+        var clickCount = 0;
+
+        view.MouseClick += (sender, args) => clickCount++;
+
+        MouseEventArgs mouseEvent = new ()
+        {
+            Position = new Point (5, 5),
+            Flags = clickFlag
+        };
+
+        // Act
+        view.NewMouseEvent (mouseEvent);
+
+        // Assert
+        Assert.Equal (1, clickCount);
+
+        view.Dispose ();
+    }
+
+    #endregion
+
+    #region Disabled View Tests
+
+    [Fact]
+    public void View_Disabled_DoesNotRaiseMouseEvent ()
+    {
+        // Arrange
+        View view = new ()
+        {
+            Width = 10,
+            Height = 10,
+            Enabled = false
+        };
+
+        var eventCalled = false;
+        view.MouseEvent += (sender, args) => { eventCalled = true; };
+
+        MouseEventArgs mouseEvent = new ()
+        {
+            Position = new Point (5, 5),
+            Flags = MouseFlags.Button1Clicked
+        };
+
+        // Act
+        view.NewMouseEvent (mouseEvent);
+
+        // Assert
+        Assert.False (eventCalled);
+
+        view.Dispose ();
+    }
+
+    [Fact]
+    public void View_Disabled_DoesNotRaiseMouseClickEvent ()
+    {
+        // Arrange
+        View view = new ()
+        {
+            Width = 10,
+            Height = 10,
+            Enabled = false
+        };
+
+        var clickCalled = false;
+        view.MouseClick += (sender, args) => { clickCalled = true; };
+
+        MouseEventArgs mouseEvent = new ()
+        {
+            Position = new Point (5, 5),
+            Flags = MouseFlags.Button1Clicked
+        };
+
+        // Act
+        view.NewMouseEvent (mouseEvent);
+
+        // Assert
+        Assert.False (clickCalled);
+
+        view.Dispose ();
+    }
+
+    #endregion
+
+    #region Focus and Selection Tests
+
+    [Theory]
+    [InlineData (true, true)]
+    [InlineData (false, false)]
+    public void MouseClick_SetsFocus_BasedOnCanFocus (bool canFocus, bool expectFocus)
+    {
+        // Arrange
+        View superView = new () { CanFocus = true, Width = 20, Height = 20 };
+        View subView = new ()
+        {
+            X = 5,
+            Y = 5,
+            Width = 10,
+            Height = 10,
+            CanFocus = canFocus
+        };
+
+        superView.Add (subView);
+        superView.SetFocus (); // Give superView focus first
+
+        MouseEventArgs mouseEvent = new ()
+        {
+            Position = new Point (2, 2),
+            Flags = MouseFlags.Button1Clicked
+        };
+
+        // Act
+        subView.NewMouseEvent (mouseEvent);
+
+        // Assert
+        Assert.Equal (expectFocus, subView.HasFocus);
+
+        subView.Dispose ();
+        superView.Dispose ();
+    }
+
+    [Fact]
+    public void MouseClick_RaisesSelecting_WhenCanFocus ()
+    {
+        // Arrange
+        View superView = new () { CanFocus = true, Width = 20, Height = 20 };
+        View view = new ()
+        {
+            X = 5,
+            Y = 5,
+            Width = 10,
+            Height = 10,
+            CanFocus = true
+        };
+
+        superView.Add (view);
+
+        var selectingCount = 0;
+        view.Selecting += (sender, args) => selectingCount++;
+
+        MouseEventArgs mouseEvent = new ()
+        {
+            Position = new Point (5, 5),
+            Flags = MouseFlags.Button1Clicked
+        };
+
+        // Act
+        view.NewMouseEvent (mouseEvent);
+
+        // Assert
+        Assert.Equal (1, selectingCount);
+
+        view.Dispose ();
+        superView.Dispose ();
+    }
+
+    #endregion
+}

+ 4 - 4
docfx/docs/multitasking.md

@@ -89,7 +89,7 @@ public class ClockView : View
         Add(timeLabel);
         Add(timeLabel);
         
         
         // Update every second
         // Update every second
-        timerToken = Application.MainLoop.AddTimeout(
+        timerToken = Application.AddTimeout(
             TimeSpan.FromSeconds(1), 
             TimeSpan.FromSeconds(1), 
             UpdateTime
             UpdateTime
         );
         );
@@ -105,7 +105,7 @@ public class ClockView : View
     {
     {
         if (disposing && timerToken != null)
         if (disposing && timerToken != null)
         {
         {
-            Application.MainLoop.RemoveTimeout(timerToken);
+            Application.RemoveTimeout(timerToken);
         }
         }
         base.Dispose(disposing);
         base.Dispose(disposing);
     }
     }
@@ -220,7 +220,7 @@ Task.Run(() =>
 ### ❌ Don't: Forget to clean up timers
 ### ❌ Don't: Forget to clean up timers
 ```csharp
 ```csharp
 // Memory leak - timer keeps running after view is disposed
 // Memory leak - timer keeps running after view is disposed
-Application.MainLoop.AddTimeout(TimeSpan.FromSeconds(1), UpdateStatus);
+Application.AddTimeout(TimeSpan.FromSeconds(1), UpdateStatus);
 ```
 ```
 
 
 ### ✅ Do: Remove timers in Dispose
 ### ✅ Do: Remove timers in Dispose
@@ -229,7 +229,7 @@ protected override void Dispose(bool disposing)
 {
 {
     if (disposing && timerToken != null)
     if (disposing && timerToken != null)
     {
     {
-        Application.MainLoop.RemoveTimeout(timerToken);
+        Application.RemoveTimeout(timerToken);
     }
     }
     base.Dispose(disposing);
     base.Dispose(disposing);
 }
 }

BIN
local_packages/Terminal.Gui.2.0.0.nupkg


BIN
local_packages/Terminal.Gui.2.0.0.snupkg