瀏覽代碼

Merge branch 'develop'

Tigger Kindel 1 年之前
父節點
當前提交
3eb585d839
共有 46 個文件被更改,包括 1627 次插入629 次删除
  1. 3 2
      .github/CODEOWNERS
  2. 5 3
      .github/workflows/dotnet-core.yml
  3. 33 36
      .github/workflows/publish.yml
  4. 2 5
      Example/Example.csproj
  5. 4 4
      FSharpExample/FSharpExample.fsproj
  6. 28 0
      GitVersion.yml
  7. 4 5
      ReactiveExample/ReactiveExample.csproj
  8. 49 8
      Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
  9. 2 2
      Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs
  10. 4 1
      Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs
  11. 18 38
      Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
  12. 29 98
      Terminal.Gui/ConsoleDrivers/NetDriver.cs
  13. 17 13
      Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
  14. 8 26
      Terminal.Gui/Core/Application.cs
  15. 17 15
      Terminal.Gui/Core/Border.cs
  16. 5 5
      Terminal.Gui/Core/ConsoleDriver.cs
  17. 8 6
      Terminal.Gui/Core/Toplevel.cs
  18. 6 9
      Terminal.Gui/Core/View.cs
  19. 8 4
      Terminal.Gui/Core/Window.cs
  20. 49 29
      Terminal.Gui/Terminal.Gui.csproj
  21. 1 1
      Terminal.Gui/Views/ContextMenu.cs
  22. 64 14
      Terminal.Gui/Views/Menu.cs
  23. 7 7
      Terminal.Gui/Views/ScrollBarView.cs
  24. 41 5
      Terminal.Gui/Views/StatusBar.cs
  25. 37 8
      Terminal.Gui/Views/TextView.cs
  26. 20 18
      Terminal.sln
  27. 6 1
      UICatalog/Properties/launchSettings.json
  28. 0 22
      UICatalog/UICatalog.cs
  29. 6 9
      UICatalog/UICatalog.csproj
  30. 0 2
      UnitTests/Application/ApplicationTests.cs
  31. 0 9
      UnitTests/AssemblyInfo.cs
  32. 24 0
      UnitTests/Core/BorderTests.cs
  33. 32 160
      UnitTests/Drivers/ConsoleDriverTests.cs
  34. 69 4
      UnitTests/Menus/ContextMenuTests.cs
  35. 65 2
      UnitTests/Menus/MenuTests.cs
  36. 8 0
      UnitTests/ReflectionTools.cs
  37. 109 1
      UnitTests/TopLevels/ToplevelTests.cs
  38. 16 0
      UnitTests/TopLevels/WindowTests.cs
  39. 10 8
      UnitTests/UnitTests.csproj
  40. 682 43
      UnitTests/Views/ScrollBarViewTests.cs
  41. 40 1
      UnitTests/Views/StatusBarTests.cs
  42. 32 2
      UnitTests/Views/TextViewTests.cs
  43. 43 0
      UnitTests/Views/ViewTests.cs
  44. 2 1
      UnitTests/xunit.runner.json
  45. 2 2
      global.json
  46. 12 0
      nuget.config

+ 3 - 2
.github/CODEOWNERS

@@ -2,7 +2,9 @@
 # the repo. Unless a later match takes precedence,
 # @global-owner1 and @global-owner2 will be requested for
 # review when someone opens a pull request.
-*       @migueldeicaza @tig
+* @tig
+
+/docfx/ @tig @tznind @bdisp
 
 # Order is important; the last matching pattern takes the most
 # precedence. When someone opens a pull request that only
@@ -29,7 +31,6 @@
 # The `docs/*` pattern will match files like
 # `docs/getting-started.md` but not further nested files like
 # `docs/build-app/troubleshooting.md`.
-#docs/*  [email protected]
 
 # In this example, @octocat owns any file in an apps directory
 # anywhere in your repository.

+ 5 - 3
.github/workflows/dotnet-core.yml

@@ -12,7 +12,6 @@ on:
       
 jobs:
   build:
-
     runs-on: ubuntu-latest
 
     steps:
@@ -22,16 +21,19 @@ jobs:
       uses: actions/setup-dotnet@v3
       with:
         dotnet-version: 7.0
+        dotnet-quality: 'ga'
 
     - name: Install dependencies
       run: |
         dotnet restore
 
     - name: Build Debug
-      run: dotnet build --configuration Debug --no-restore
-
+      run: |
+        dotnet build --configuration Debug --no-restore
+        
     - name: Test
       run: |
+        sed -i 's/"stopOnFail": false/"stopOnFail": true/g' UnitTests/xunit.runner.json
         dotnet test --no-restore --verbosity normal --collect:"XPlat Code Coverage"  --settings UnitTests/coverlet.runsettings
         mv -v UnitTests/TestResults/*/*.* UnitTests/TestResults/
 

+ 33 - 36
.github/workflows/publish.yml

@@ -1,11 +1,14 @@
 name: Publish Terminal.Gui
+
 on:
   push:
+    branches: [ main, develop, v2_release, v2_develop ]
     tags:
       - v*
+    paths-ignore:
+      - '**.md'
 
 jobs:
-
   publish:
     name: Build and Publish to Nuget.org
     runs-on: ubuntu-latest
@@ -13,27 +16,27 @@ jobs:
     steps:
     - uses: actions/checkout@v3
       with:
-        fetch-depth: 0 #fetch-depth is needed for GitVersion
+        fetch-depth: 0 # fetch-depth is needed for GitVersion
 
-    - name: Install and calculate the new version with GitVersion 
+    - name: Install GitVersion 
       uses: gittools/actions/gitversion/setup@v0
       with:
-        versionSpec: 5.x
+          versionSpec: '5.x'
+          includePrerelease: true
 
     - name: Determine Version
       uses: gittools/actions/gitversion/execute@v0
+      with:
+        useConfigFile: true
+        #additionalArguments: /b develop
       id: gitversion # step id used as reference for output values
 
-    - name: Display GitVersion outputs
-      run: |
-        echo "Version: ${{ steps.gitversion.outputs.SemVer }}"
-        echo "CommitsSinceVersionSource: ${{ steps.gitversion.outputs.CommitsSinceVersionSource }}"
-
     - name: Setup dotnet
       uses: actions/setup-dotnet@v3
       with:
         dotnet-version: 7.0
-
+        dotnet-quality: 'ga'
+        
     - name: Install dependencies
       run: dotnet restore
 
@@ -45,33 +48,27 @@ jobs:
     - name: Pack
       run: dotnet pack -c Release --include-symbols -p:Version='${{ steps.gitversion.outputs.SemVer }}' 
 
-    - name: Test to generate Code Coverage Report
-      run: |
-        dotnet test --verbosity normal --collect:"XPlat Code Coverage" --settings UnitTests/coverlet.runsettings
-        mv -v UnitTests/TestResults/*/*.* UnitTests/TestResults/
+    # - name: Test to generate Code Coverage Report
+    #   run: |
+    #     sed -i 's/"stopOnFail": false/"stopOnFail": true/g' UnitTests/xunit.runner.json
+    #     dotnet test --verbosity normal --collect:"XPlat Code Coverage" --settings UnitTests/coverlet.runsettings
+    #     mv -v UnitTests/TestResults/*/*.* UnitTests/TestResults/
 
-    - name: Create Test Coverage Badge
-      uses: simon-k/[email protected]
-      id: create_coverage_badge
-      with:
-        label: Unit Test Coverage
-        color: brightgreen
-        path: UnitTests/TestResults/coverage.opencover.xml
-        gist-filename: code-coverage.json
-        # https://gist.github.com/migueldeicaza/90ef67a684cb71db1817921a970f8d27
-        gist-id: 90ef67a684cb71db1817921a970f8d27
-        gist-auth-token: ${{ secrets.GIST_AUTH_TOKEN }}   
+    # - name: Create Test Coverage Badge
+    #   uses: simon-k/[email protected]
+    #   id: create_coverage_badge
+    #   with:
+    #     label: Unit Test Coverage
+    #     color: brightgreen
+    #     path: UnitTests/TestResults/coverage.opencover.xml
+    #     gist-filename: code-coverage.json
+    #     gist-id: 90ef67a684cb71db1817921a970f8d27
+    #     gist-auth-token: ${{ secrets.GIST_AUTH_TOKEN }}   
 
-    - name: Print Code Coverage
-      run: |
-        echo "Code coverage percentage: ${{steps.create_coverage_badge.outputs.percentage}}%"
-        echo "Badge data: ${{steps.create_coverage_badge.outputs.badge}}"
+    # - name: Print Code Coverage
+    #   run: |
+    #     echo "Code coverage percentage: ${{steps.create_coverage_badge.outputs.percentage}}%"
+    #     echo "Badge data: ${{steps.create_coverage_badge.outputs.badge}}"
         
-    #- name: Prep GitHub Packages
-    #  run: dotnet nuget add source --username tig --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/tig/index.json"
-
-    #- name: Publish to GitHub packages 
-    #  run: dotnet nuget push NStack/bin/Release/*.nupkg --api-key ${{ secrets.GITHUB_TOKEN }}  --source "github"
-
     - name: Publish to NuGet.org
-      run: dotnet nuget push Terminal.Gui/bin/Release/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json
+      run: dotnet nuget push Terminal.Gui/bin/Release/Terminal.Gui.${{ steps.gitversion.outputs.SemVer }}.nupkg --api-key ${{ secrets.NUGET_API_KEY }} 

+ 2 - 5
Example/Example.csproj

@@ -3,12 +3,9 @@
     <OutputType>Exe</OutputType>
     <TargetFramework>net7.0</TargetFramework>
     <!-- Version numbers are automatically updated by gitversion when a release is released -->
-    <!-- In the source tree the version will always be 1.0 for all projects. -->
     <!-- Do not modify these. -->
-    <AssemblyVersion>1.10.1.0</AssemblyVersion>
-    <FileVersion>1.10.1.0</FileVersion>
-    <Version>1.10.1</Version>
-    <InformationalVersion>1.10.1+6.Branch.main.Sha.f7ee66ddbf8dbcfb0d96af7d63789879091670ec</InformationalVersion>
+    <FileVersion>1.0.0.0</FileVersion>
+    <Version>1.0.0</Version>
   </PropertyGroup>
   <ItemGroup>
     <ProjectReference Include="..\Terminal.Gui\Terminal.Gui.csproj" />

+ 4 - 4
FSharpExample/FSharpExample.fsproj

@@ -2,10 +2,10 @@
   <PropertyGroup>
     <OutputType>Exe</OutputType>
     <TargetFramework>net6.0</TargetFramework>
-    <AssemblyVersion>1.10.1.0</AssemblyVersion>
-    <FileVersion>1.10.1.0</FileVersion>
-    <InformationalVersion>1.10.1+6.Branch.main.Sha.f7ee66ddbf8dbcfb0d96af7d63789879091670ec</InformationalVersion>
-    <Version>1.10.1</Version>
+    <AssemblyVersion>1.14.0.0</AssemblyVersion>
+    <FileVersion>1.14.0.0</FileVersion>
+    <InformationalVersion>1.14.0-pre.1+Branch.develop.Sha.e0b7464669ef87b96b57f2285200e02bcf85d0e7</InformationalVersion>
+    <Version>1.14.0-pre0001</Version>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="Program.fs" />

+ 28 - 0
GitVersion.yml

@@ -0,0 +1,28 @@
+mode: ContinuousDeployment
+tag-prefix: '[vV]'
+continuous-delivery-fallback-tag: pre
+branches:
+  develop:
+    mode: ContinuousDeployment
+    tag: pre
+    regex: develop
+    source-branches:
+    - main
+    pre-release-weight: 100
+  main:
+    tag: rc
+    increment: Patch
+    source-branches:
+    - develop
+    - main
+  feature:
+    tag: useBranchName
+    regex: ^features?[/-]
+    source-branches:
+    - develop
+    - main
+  pull-request:
+    tag: PullRequest.{BranchName}
+    increment: Inherit
+ignore:
+  sha: []

+ 4 - 5
ReactiveExample/ReactiveExample.csproj

@@ -3,12 +3,11 @@
     <OutputType>Exe</OutputType>
     <TargetFramework>net7.0</TargetFramework>
     <!-- Version numbers are automatically updated by gitversion when a release is released -->
-    <!-- In the source tree the version will always be 2.0 for all projects. -->
     <!-- Do not modify these. -->
-    <AssemblyVersion>1.10.1.0</AssemblyVersion>
-    <FileVersion>1.10.1.0</FileVersion>
-    <Version>1.10.1</Version>
-    <InformationalVersion>1.10.1+6.Branch.main.Sha.f7ee66ddbf8dbcfb0d96af7d63789879091670ec</InformationalVersion>
+    <!--<AssemblyVersion>1.14.0.0</AssemblyVersion>
+    <FileVersion>1.14.0.0</FileVersion>
+    <Version>1.14.0-pre0001</Version>
+    <InformationalVersion>1.14.0-pre.1+Branch.develop.Sha.e0b7464669ef87b96b57f2285200e02bcf85d0e7</InformationalVersion>-->
   </PropertyGroup>
   <ItemGroup>
     <PackageReference Include="ReactiveUI.Fody" Version="19.4.1" />

+ 49 - 8
Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs

@@ -20,12 +20,10 @@ namespace Terminal.Gui {
 		public override int Rows => Curses.Lines;
 		public override int Left => 0;
 		public override int Top => 0;
+		[Obsolete ("This API is deprecated", false)]
 		public override bool EnableConsoleScrolling { get; set; }
-		[Obsolete ("This API is deprecated; use EnableConsoleScrolling instead.", false)]
-		public override bool HeightAsBuffer {
-			get => EnableConsoleScrolling;
-			set => EnableConsoleScrolling = value;
-		}
+		[Obsolete ("This API is deprecated", false)]
+		public override bool HeightAsBuffer { get; set; }
 		public override IClipboard Clipboard { get => clipboard; }
 
 		CursorVisibility? initialCursorVisibility = null;
@@ -144,7 +142,6 @@ namespace Terminal.Gui {
 			Curses.raw ();
 			Curses.noecho ();
 			Curses.refresh ();
-			ProcessWinChange ();
 		}
 
 		private void ProcessWinChange ()
@@ -163,6 +160,10 @@ namespace Terminal.Gui {
 			StopReportingMouseMoves ();
 			SetCursorVisibility (CursorVisibility.Default);
 
+			// throws away any typeahead that has been typed by
+			// the user and has not yet been read by the program.
+			Curses.flushinp ();
+
 			Curses.endwin ();
 		}
 
@@ -339,8 +340,12 @@ namespace Terminal.Gui {
 			Key k = Key.Null;
 
 			if (code == Curses.KEY_CODE_YES) {
-				if (wch == Curses.KeyResize) {
+				while (code == Curses.KEY_CODE_YES && wch == Curses.KeyResize) {
 					ProcessWinChange ();
+					code = Curses.get_wch (out wch);
+				}
+				if (wch == 0) {
+					return;
 				}
 				if (wch == Curses.KeyMouse) {
 					int wch2 = wch;
@@ -494,8 +499,44 @@ namespace Terminal.Gui {
 			}
 		}
 
+		MouseFlags lastMouseFlags;
+
 		void ProcessMouseEvent (MouseFlags mouseFlag, Point pos)
 		{
+			bool WasButtonReleased (MouseFlags flag)
+			{
+				return flag.HasFlag (MouseFlags.Button1Released) ||
+					flag.HasFlag (MouseFlags.Button2Released) ||
+					flag.HasFlag (MouseFlags.Button3Released) ||
+					flag.HasFlag (MouseFlags.Button4Released);
+			}
+
+			bool IsButtonNotPressed (MouseFlags flag)
+			{
+				return !flag.HasFlag (MouseFlags.Button1Pressed) &&
+					!flag.HasFlag (MouseFlags.Button2Pressed) &&
+					!flag.HasFlag (MouseFlags.Button3Pressed) &&
+					!flag.HasFlag (MouseFlags.Button4Pressed);
+			}
+
+			bool IsButtonClickedOrDoubleClicked (MouseFlags flag)
+			{
+				return flag.HasFlag (MouseFlags.Button1Clicked) ||
+					flag.HasFlag (MouseFlags.Button2Clicked) ||
+					flag.HasFlag (MouseFlags.Button3Clicked) ||
+					flag.HasFlag (MouseFlags.Button4Clicked) ||
+					flag.HasFlag (MouseFlags.Button1DoubleClicked) ||
+					flag.HasFlag (MouseFlags.Button2DoubleClicked) ||
+					flag.HasFlag (MouseFlags.Button3DoubleClicked) ||
+					flag.HasFlag (MouseFlags.Button4DoubleClicked);
+			}
+
+			if ((WasButtonReleased (mouseFlag) && IsButtonNotPressed (lastMouseFlags)) ||
+				(IsButtonClickedOrDoubleClicked (mouseFlag) && lastMouseFlags == 0)) {
+				return;
+			}
+
+			lastMouseFlags = mouseFlag;
 			var me = new MouseEvent () {
 				Flags = mouseFlag,
 				X = pos.X,
@@ -531,7 +572,7 @@ namespace Terminal.Gui {
 			});
 
 			mLoop.WinChanged += () => {
-				ProcessWinChange ();
+				ProcessInput ();
 			};
 		}
 

+ 2 - 2
Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs

@@ -97,8 +97,8 @@ namespace Terminal.Gui {
 		{
 			this.mainLoop = mainLoop;
 			pipe (wakeupPipes);
-			AddWatch (wakeupPipes [0], Condition.PollIn, ml => {
-				read (wakeupPipes [0], ignore, readHandle);
+			AddWatch (wakeupPipes [1], Condition.PollIn, ml => {
+				read (wakeupPipes [1], ignore, readHandle);
 				return true;
 			});
 		}

+ 4 - 1
Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs

@@ -160,7 +160,10 @@ namespace Unix.Terminal {
 
 			console_sharp_get_dims (out l, out c);
 
-			if (l == 1 || l != lines || c != cols) {
+			if (l < 1) {
+				l = 1;
+			}
+			if (l != lines || c != cols) {
 				lines = l;
 				cols = c;
 				return true;

+ 18 - 38
Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs

@@ -45,12 +45,10 @@ namespace Terminal.Gui {
 		// Only handling left here because not all terminals has a horizontal scroll bar.
 		public override int Left => 0;
 		public override int Top => 0;
+		[Obsolete ("This API is deprecated", false)]
 		public override bool EnableConsoleScrolling { get; set; }
-		[Obsolete ("This API is deprecated; use EnableConsoleScrolling instead.", false)]
-		public override bool HeightAsBuffer {
-			get => EnableConsoleScrolling;
-			set => EnableConsoleScrolling = value;
-		}
+		[Obsolete ("This API is deprecated", false)]
+		public override bool HeightAsBuffer { get; set; }
 		private IClipboard clipboard = null;
 		public override IClipboard Clipboard => clipboard;
 
@@ -537,31 +535,24 @@ namespace Terminal.Gui {
 			FakeConsole.SetBufferSize (width, height);
 			cols = width;
 			rows = height;
-			if (!EnableConsoleScrolling) {
-				SetWindowSize (width, height);
-			}
+			SetWindowSize (width, height);
 			ProcessResize ();
 		}
 
 		public void SetWindowSize (int width, int height)
 		{
 			FakeConsole.SetWindowSize (width, height);
-			if (!EnableConsoleScrolling) {
-				if (width != cols || height != rows) {
-					SetBufferSize (width, height);
-					cols = width;
-					rows = height;
-				}
+			if (width != cols || height != rows) {
+				SetBufferSize (width, height);
+				cols = width;
+				rows = height;
 			}
 			ProcessResize ();
 		}
 
 		public void SetWindowPosition (int left, int top)
 		{
-			if (EnableConsoleScrolling) {
-				this.left = Math.Max (Math.Min (left, Cols - FakeConsole.WindowWidth), 0);
-				this.top = Math.Max (Math.Min (top, Rows - FakeConsole.WindowHeight), 0);
-			} else if (this.left > 0 || this.top > 0) {
+			if (this.left > 0 || this.top > 0) {
 				this.left = 0;
 				this.top = 0;
 			}
@@ -577,29 +568,18 @@ namespace Terminal.Gui {
 
 		public override void ResizeScreen ()
 		{
-			if (!EnableConsoleScrolling) {
-				if (FakeConsole.WindowHeight > 0) {
-					// Can raise an exception while is still resizing.
-					try {
-#pragma warning disable CA1416
-						FakeConsole.CursorTop = 0;
-						FakeConsole.CursorLeft = 0;
-						FakeConsole.WindowTop = 0;
-						FakeConsole.WindowLeft = 0;
-#pragma warning restore CA1416
-					} catch (System.IO.IOException) {
-						return;
-					} catch (ArgumentOutOfRangeException) {
-						return;
-					}
-				}
-			} else {
+			if (FakeConsole.WindowHeight > 0) {
+				// Can raise an exception while is still resizing.
 				try {
 #pragma warning disable CA1416
-					FakeConsole.WindowLeft = Math.Max (Math.Min (left, Cols - FakeConsole.WindowWidth), 0);
-					FakeConsole.WindowTop = Math.Max (Math.Min (top, Rows - FakeConsole.WindowHeight), 0);
+					FakeConsole.CursorTop = 0;
+					FakeConsole.CursorLeft = 0;
+					FakeConsole.WindowTop = 0;
+					FakeConsole.WindowLeft = 0;
 #pragma warning restore CA1416
-				} catch (Exception) {
+				} catch (System.IO.IOException) {
+					return;
+				} catch (ArgumentOutOfRangeException) {
 					return;
 				}
 			}

+ 29 - 98
Terminal.Gui/ConsoleDrivers/NetDriver.cs

@@ -113,7 +113,6 @@ namespace Terminal.Gui {
 		ConsoleDriver consoleDriver;
 		volatile ConsoleKeyInfo [] cki = null;
 		static volatile bool isEscSeq;
-		int lastWindowHeight;
 		bool stopTasks;
 #if PROCESS_REQUEST
 		bool neededProcessRequest;
@@ -251,21 +250,11 @@ namespace Terminal.Gui {
 
 		bool IsWinChanged (int winHeight, int winWidth, int buffHeight, int buffWidth)
 		{
-			if (!consoleDriver.EnableConsoleScrolling) {
-				if (winWidth != consoleDriver.Cols || winHeight != consoleDriver.Rows) {
-					var w = Math.Max (winWidth, 0);
-					var h = Math.Max (winHeight, 0);
-					GetWindowSizeEvent (new Size (w, h));
-					return true;
-				}
-			} else {
-				if (winWidth != consoleDriver.Cols || winHeight != lastWindowHeight
-					|| buffWidth != consoleDriver.Cols || buffHeight != consoleDriver.Rows) {
-
-					lastWindowHeight = Math.Max (winHeight, 0);
-					GetWindowSizeEvent (new Size (winWidth, lastWindowHeight));
-					return true;
-				}
+			if (winWidth != consoleDriver.Cols || winHeight != consoleDriver.Rows) {
+				var w = Math.Max (winWidth, 0);
+				var h = Math.Max (winHeight, 0);
+				GetWindowSizeEvent (new Size (w, h));
+				return true;
 			}
 			return false;
 		}
@@ -584,20 +573,15 @@ namespace Terminal.Gui {
 		public override int Rows => rows;
 		public override int Left => left;
 		public override int Top => top;
+		[Obsolete ("This API is deprecated", false)]
 		public override bool EnableConsoleScrolling { get; set; }
-		[Obsolete ("This API is deprecated; use EnableConsoleScrolling instead.", false)]
-		public override bool HeightAsBuffer {
-			get => EnableConsoleScrolling;
-			set => EnableConsoleScrolling = value;
-		}
-
+		[Obsolete ("This API is deprecated", false)]
+		public override bool HeightAsBuffer { get; set; }
 		public NetWinVTConsole NetWinConsole { get; }
 		public bool IsWinPlatform { get; }
 		public override IClipboard Clipboard { get; }
 		public override int [,,] Contents => contents;
 
-		int largestBufferHeight;
-
 		public NetDriver ()
 		{
 			var p = Environment.OSVersion.Platform;
@@ -752,14 +736,8 @@ namespace Terminal.Gui {
 
 			Console.TreatControlCAsInput = true;
 
-			if (EnableConsoleScrolling) {
-				largestBufferHeight = Console.BufferHeight;
-			} else {
-				largestBufferHeight = Console.WindowHeight;
-			}
-
 			cols = Console.WindowWidth;
-			rows = largestBufferHeight;
+			rows = Console.WindowHeight; 
 
 			CurrentAttribute = MakeColor (Color.White, Color.Black);
 			InitalizeColorSchemes ();
@@ -775,54 +753,31 @@ namespace Terminal.Gui {
 
 		public override void ResizeScreen ()
 		{
-			if (!EnableConsoleScrolling) {
-				if (Console.WindowHeight > 0) {
-					// Not supported on Unix.
-					if (IsWinPlatform) {
-						// Can raise an exception while is still resizing.
-						try {
-#pragma warning disable CA1416
-							Console.CursorTop = 0;
-							Console.CursorLeft = 0;
-							Console.WindowTop = 0;
-							Console.WindowLeft = 0;
-							if (Console.WindowHeight > Rows) {
-								Console.SetWindowSize (Cols, Rows);
-							}
-							Console.SetBufferSize (Cols, Rows);
-#pragma warning restore CA1416
-						} catch (System.IO.IOException) {
-							setClip ();
-						} catch (ArgumentOutOfRangeException) {
-							setClip ();
-						}
-					} else {
-						Console.Out.Write ($"\x1b[8;{Rows};{Cols}t");
-					}
-				}
-			} else {
+			if (Console.WindowHeight > 0) {
+				// Not supported on Unix.
 				if (IsWinPlatform) {
-					if (Console.WindowHeight > 0) {
-						// Can raise an exception while is still resizing.
-						try {
+					// Can raise an exception while is still resizing.
+					try {
 #pragma warning disable CA1416
-							Console.CursorTop = 0;
-							Console.CursorLeft = 0;
-							if (Console.WindowHeight > Rows) {
-								Console.SetWindowSize (Cols, Rows);
-							}
-							Console.SetBufferSize (Cols, Rows);
-#pragma warning restore CA1416
-						} catch (System.IO.IOException) {
-							setClip ();
-						} catch (ArgumentOutOfRangeException) {
-							setClip ();
+						Console.CursorTop = 0;
+						Console.CursorLeft = 0;
+						Console.WindowTop = 0;
+						Console.WindowLeft = 0;
+						if (Console.WindowHeight > Rows) {
+							Console.SetWindowSize (Cols, Rows);
 						}
+						Console.SetBufferSize (Cols, Rows);
+#pragma warning restore CA1416
+					} catch (System.IO.IOException) {
+						setClip ();
+					} catch (ArgumentOutOfRangeException) {
+						setClip ();
 					}
 				} else {
-					Console.Out.Write ($"\x1b[30;{Rows};{Cols}t");
+					Console.Out.Write ($"\x1b[8;{Rows};{Cols}t");
 				}
 			}
+
 			setClip ();
 
 			void setClip ()
@@ -864,9 +819,7 @@ namespace Terminal.Gui {
 
 		public override void UpdateScreen ()
 		{
-			if (winChanging || Console.WindowHeight < 1 || contents.Length != Rows * Cols * 3
-				|| (!EnableConsoleScrolling && Rows != Console.WindowHeight)
-				|| (EnableConsoleScrolling && Rows != largestBufferHeight)) {
+			if (winChanging || Console.WindowHeight < 1 || contents.Length != Rows * Cols * 3 || Rows != Console.WindowHeight) {
 				return;
 			}
 
@@ -1021,23 +974,6 @@ namespace Terminal.Gui {
 
 		private void SetWindowPosition (int col, int row)
 		{
-			if (IsWinPlatform && EnableConsoleScrolling) {
-				var winTop = Math.Max (Rows - Console.WindowHeight - row, 0);
-				winTop = Math.Min (winTop, Rows - Console.WindowHeight + 1);
-				winTop = Math.Max (winTop, 0);
-				if (winTop != Console.WindowTop) {
-					try {
-						if (!EnsureBufferSize ()) {
-							return;
-						}
-#pragma warning disable CA1416
-						Console.SetWindowPosition (col, winTop);
-#pragma warning restore CA1416
-					} catch (System.IO.IOException) {
-
-					} catch (System.ArgumentOutOfRangeException) { }
-				}
-			}
 			top = Console.WindowTop;
 			left = Console.WindowLeft;
 		}
@@ -1287,15 +1223,10 @@ namespace Terminal.Gui {
 		void ChangeWin (Size size)
 		{
 			winChanging = true;
-			if (!EnableConsoleScrolling) {
-				largestBufferHeight = Math.Max (size.Height, 0);
-			} else {
-				largestBufferHeight = Math.Max (size.Height, largestBufferHeight);
-			}
 			top = 0;
 			left = 0;
 			cols = size.Width;
-			rows = largestBufferHeight;
+			rows = Math.Max (size.Height, 0); 
 			ResizeScreen ();
 			UpdateOffScreen ();
 			winChanging = false;

+ 17 - 13
Terminal.Gui/ConsoleDrivers/WindowsDriver.cs

@@ -709,17 +709,16 @@ namespace Terminal.Gui {
 		WindowsConsole.SmallRect damageRegion;
 		IClipboard clipboard;
 		int [,,] contents;
+		readonly bool isWindowsTerminal;
 
 		public override int Cols => cols;
 		public override int Rows => rows;
 		public override int Left => left;
 		public override int Top => top;
+		[Obsolete ("This API is deprecated", false)]
 		public override bool EnableConsoleScrolling { get; set; }
-		[Obsolete ("This API is deprecated; use EnableConsoleScrolling instead.", false)]
-		public override bool HeightAsBuffer {
-			get => EnableConsoleScrolling;
-			set => EnableConsoleScrolling = value;
-		}
+		[Obsolete ("This API is deprecated", false)]
+		public override bool HeightAsBuffer { get; set; }
 		public override IClipboard Clipboard => clipboard;
 		public override int [,,] Contents => contents;
 
@@ -734,6 +733,8 @@ namespace Terminal.Gui {
 		{
 			WinConsole = new WindowsConsole ();
 			clipboard = new WindowsClipboard ();
+
+			isWindowsTerminal = Environment.GetEnvironmentVariable ("WT_SESSION") != null;
 		}
 
 		public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
@@ -1428,15 +1429,17 @@ namespace Terminal.Gui {
 
 			try {
 				// Needed for Windows Terminal
-				// ESC [ ? 1047 h  Activate xterm alternative buffer (no backscroll)
-				// ESC [ ? 1047 l  Restore xterm working buffer (with backscroll)
+				// ESC [ ? 1047 h  Save cursor position and activate xterm alternative buffer (no backscroll)
+				// ESC [ ? 1047 l  Restore cursor position and restore xterm working buffer (with backscroll)
 				// ESC [ ? 1048 h  Save cursor position
 				// ESC [ ? 1048 l  Restore cursor position
-				// ESC [ ? 1049 h  Save cursor position and activate xterm alternative buffer (no backscroll)
-				// ESC [ ? 1049 l  Restore cursor position and restore xterm working buffer (with backscroll)
-				// Per Issue #2264 using the alterantive screen buffer is required for Windows Terminal to not 
+				// ESC [ ? 1049 h  Activate xterm alternative buffer (no backscroll)
+				// ESC [ ? 1049 l  Restore xterm working buffer (with backscroll)
+				// Per Issue #2264 using the alternative screen buffer is required for Windows Terminal to not 
 				// wipe out the backscroll buffer when the application exits.
-				Console.Out.Write ("\x1b[?1047h");
+				if (isWindowsTerminal) {
+					Console.Out.Write ("\x1b[?1049h");
+				}
 
 				var winSize = WinConsole.GetConsoleOutputWindow (out Point pos);
 				cols = winSize.Width;
@@ -1678,12 +1681,13 @@ namespace Terminal.Gui {
 
 		public override void End ()
 		{
-
 			WinConsole.Cleanup ();
 			WinConsole = null;
 
 			// Disable alternative screen buffer.
-			Console.Out.Write ("\x1b[?1047l");
+			if (isWindowsTerminal) {
+				Console.Out.Write ("\x1b[?1049l");
+			}
 		}
 
 		/// <inheritdoc/>

+ 8 - 26
Terminal.Gui/Core/Application.cs

@@ -111,8 +111,6 @@ namespace Terminal.Gui {
 		/// </summary>
 		public static View WantContinuousButtonPressedView { get; private set; }
 
-		private static bool? _enableConsoleScrolling;
-
 		/// <summary>
 		/// The current <see cref="ConsoleDriver.EnableConsoleScrolling"/> used in the terminal.
 		/// </summary>
@@ -128,32 +126,17 @@ namespace Terminal.Gui {
 		/// In this case console scrolling is enabled and the contents (<see cref="ConsoleDriver.Rows"/> high) will scroll
 		/// as the console scrolls. 
 		/// </para>
-		/// This API was previously named 'HeightAsBuffer` but was renamed to make its purpose more clear.
+		/// <para>This API is deprecated and has no impact when enabled.</para>
+		/// <para>This API was previously named 'HeightAsBuffer` but was renamed to make its purpose more clear.</para>
 		/// </remarks>
-		public static bool EnableConsoleScrolling {
-			get {
-				if (Driver == null) {
-					return _enableConsoleScrolling.HasValue && _enableConsoleScrolling.Value;
-				}
-				return Driver.EnableConsoleScrolling;
-			}
-			set {
-				_enableConsoleScrolling = value;
-				if (Driver == null) {
-					return;
-				}
-				Driver.EnableConsoleScrolling = value;
-			}
-		}
+		[Obsolete ("This API is deprecated and has no impact when enabled.", false)]
+		public static bool EnableConsoleScrolling { get; set; }
 
 		/// <summary>
 		/// This API is deprecated; use <see cref="EnableConsoleScrolling"/> instead.
 		/// </summary>
-		[Obsolete ("This API is deprecated; use EnableConsoleScrolling instead.", false)]
-		public static bool HeightAsBuffer {
-			get => EnableConsoleScrolling;
-			set => EnableConsoleScrolling = value;
-		}
+		[Obsolete ("This API is deprecated and has no impact when enabled.", false)]
+		public static bool HeightAsBuffer { get; set; }
 
 		static Key alternateForwardKey = Key.PageDown | Key.CtrlMask;
 
@@ -449,7 +432,6 @@ namespace Terminal.Gui {
 			MainLoop = new MainLoop (mainLoopDriver);
 
 			try {
-				Driver.EnableConsoleScrolling = EnableConsoleScrolling;
 				Driver.Init (TerminalResized);
 			} catch (InvalidOperationException ex) {
 				// This is a case where the driver is unable to initialize the console.
@@ -1071,7 +1053,8 @@ namespace Terminal.Gui {
 					MdiTop.OnAllChildClosed ();
 				} else {
 					SetCurrentAsTop ();
-					Current.OnEnter (Current);
+					runState.Toplevel.OnLeave (Current);
+					Current.OnEnter (runState.Toplevel);
 				}
 				Refresh ();
 			}
@@ -1125,7 +1108,6 @@ namespace Terminal.Gui {
 			NotifyStopRunState = null;
 			_initialized = false;
 			mouseGrabView = null;
-			_enableConsoleScrolling = false;
 
 			// Reset synchronization context to allow the user to run async/await,
 			// as the main loop has been ended, the synchronization context from 

+ 17 - 15
Terminal.Gui/Core/Border.cs

@@ -284,7 +284,7 @@ namespace Terminal.Gui {
 			/// <inheritdoc/>
 			public override void OnCanFocusChanged ()
 			{
-				if (Border.Child != null) {
+				if (Border?.Child != null) {
 					Border.Child.CanFocus = CanFocus;
 				}
 				base.OnCanFocusChanged ();
@@ -375,8 +375,10 @@ namespace Terminal.Gui {
 		public Color BorderBrush {
 			get => borderBrush != null ? (Color)borderBrush : (Color)(-1);
 			set {
-				borderBrush = value;
-				OnBorderChanged ();
+				if (Enum.IsDefined (typeof (Color), value)) {
+					borderBrush = value;
+					OnBorderChanged ();
+				}
 			}
 		}
 
@@ -386,8 +388,10 @@ namespace Terminal.Gui {
 		public Color Background {
 			get => background != null ? (Color)background : (Color)(-1);
 			set {
-				background = value;
-				OnBorderChanged ();
+				if (Enum.IsDefined (typeof (Color), value)) {
+					background = value;
+					OnBorderChanged ();
+				}
 			}
 		}
 
@@ -445,12 +449,10 @@ namespace Terminal.Gui {
 
 		private void Parent_Removed (View obj)
 		{
-			if (borderBrush != null)
-			{
+			if (borderBrush != null) {
 				BorderBrush = default;
 			}
-			if (background != null)
-			{
+			if (background != null) {
 				Background = default;
 			}
 			child.Removed -= Parent_Removed;
@@ -800,7 +802,7 @@ namespace Terminal.Gui {
 			SetBorderBrush (driver);
 
 			// Draw the upper BorderThickness
-			for (int r = frame.Y;
+			for (int r = Math.Max (frame.Y, 0);
 				r < Math.Min (frame.Y + borderThickness.Top, frame.Bottom); r++) {
 				for (int c = frame.X;
 					c < Math.Min (frame.Right, driver.Cols); c++) {
@@ -810,7 +812,7 @@ namespace Terminal.Gui {
 			}
 
 			// Draw the left BorderThickness
-			for (int r = Math.Min (frame.Y + borderThickness.Top, frame.Bottom);
+			for (int r = Math.Max (Math.Min (frame.Y + borderThickness.Top, frame.Bottom), 0);
 				r < Math.Min (frame.Bottom - borderThickness.Bottom, driver.Rows); r++) {
 				for (int c = frame.X;
 					c < Math.Min (frame.X + borderThickness.Left, frame.Right); c++) {
@@ -820,7 +822,7 @@ namespace Terminal.Gui {
 			}
 
 			// Draw the right BorderThickness
-			for (int r = Math.Min (frame.Y + borderThickness.Top, frame.Bottom);
+			for (int r = Math.Max (Math.Min (frame.Y + borderThickness.Top, frame.Bottom), 0);
 				r < Math.Min (frame.Bottom - borderThickness.Bottom, driver.Rows); r++) {
 				for (int c = Math.Max (frame.Right - borderThickness.Right, frame.X);
 					c < Math.Min (frame.Right, driver.Cols); c++) {
@@ -842,7 +844,7 @@ namespace Terminal.Gui {
 			SetBackground (driver);
 
 			// Draw the upper Padding
-			for (int r = frame.Y + borderThickness.Top;
+			for (int r = Math.Max (frame.Y + borderThickness.Top, 0);
 				r < Math.Min (frame.Y + sumThickness.Top, frame.Bottom - borderThickness.Bottom); r++) {
 				for (int c = frame.X + borderThickness.Left;
 					c < Math.Min (frame.Right - borderThickness.Right, driver.Cols); c++) {
@@ -852,7 +854,7 @@ namespace Terminal.Gui {
 			}
 
 			// Draw the left Padding
-			for (int r = frame.Y + sumThickness.Top;
+			for (int r = Math.Max (frame.Y + sumThickness.Top, 0);
 				r < Math.Min (frame.Bottom - sumThickness.Bottom, driver.Rows); r++) {
 				for (int c = frame.X + borderThickness.Left;
 					c < Math.Min (frame.X + sumThickness.Left, frame.Right - borderThickness.Right); c++) {
@@ -862,7 +864,7 @@ namespace Terminal.Gui {
 			}
 
 			// Draw the right Padding
-			for (int r = frame.Y + sumThickness.Top;
+			for (int r = Math.Max (frame.Y + sumThickness.Top, 0);
 				r < Math.Min (frame.Bottom - sumThickness.Bottom, driver.Rows); r++) {
 				for (int c = Math.Max (frame.Right - sumThickness.Right, frame.X + sumThickness.Left);
 					c < Math.Max (frame.Right - borderThickness.Right, frame.X + sumThickness.Left); c++) {

+ 5 - 5
Terminal.Gui/Core/ConsoleDriver.cs

@@ -696,16 +696,16 @@ namespace Terminal.Gui {
 		/// </para>
 		/// </summary>
 		/// <remarks>
-		/// NOTE: This functionaliy is currently broken on Windows Terminal.
+		/// NOTE: Changes to Windows Terminal prevents this functionality from working. It only really worked on Windows 'conhost' previously.
 		/// </remarks>
+		[Obsolete ("This API is deprecated and has no impact when enabled.", false)]
 		public abstract bool EnableConsoleScrolling { get; set; }
-
 		/// <summary>
-		/// This API is deprecated; use <see cref="EnableConsoleScrolling"/> instead.
+		/// This API is deprecated and has no impact when enabled.
 		/// </summary>
-		[Obsolete ("This API is deprecated; use EnableConsoleScrolling instead.", false)]
+		[Obsolete ("This API is deprecated and has no impact when enabled.", false)]
 		public abstract bool HeightAsBuffer { get; set; }
-
+		
 		/// <summary>
 		/// The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag
 		/// </summary>

+ 8 - 6
Terminal.Gui/Core/Toplevel.cs

@@ -552,6 +552,7 @@ namespace Terminal.Gui {
 		///<inheritdoc/>
 		public override void Add (View view)
 		{
+			CanFocus = true;
 			AddMenuStatusBar (view);
 			base.Add (view);
 		}
@@ -603,7 +604,7 @@ namespace Terminal.Gui {
 			out int nx, out int ny, out View mb, out View sb)
 		{
 			int l;
-			View superView;
+			View superView = null;
 			if (top?.SuperView == null || top == Application.Top || top?.SuperView == Application.Top) {
 				l = Driver.Cols;
 				superView = Application.Top;
@@ -618,11 +619,12 @@ namespace Terminal.Gui {
 				nx = Math.Max (top.Frame.Right - mfLength, 0);
 			}
 			//System.Diagnostics.Debug.WriteLine ($"nx:{nx}, rWidth:{rWidth}");
-			bool m, s;
-			if (top?.SuperView == null || top == Application.Top || top?.SuperView == Application.Top) {
+			bool m = false, s = false;
+			mb = null; sb = null;
+			if (!(top is Window && top == Application.Top) && (top?.SuperView == null || top == Application.Top || top?.SuperView == Application.Top)) {
 				m = Application.Top.MenuBar?.Visible == true;
 				mb = Application.Top.MenuBar;
-			} else {
+			} else if (!(top is Window && top == Application.Top)) {
 				var t = top.SuperView;
 				while (!(t is Toplevel)) {
 					t = t.SuperView;
@@ -636,10 +638,10 @@ namespace Terminal.Gui {
 				l = 0;
 			}
 			ny = Math.Max (y, l);
-			if (top?.SuperView == null || top == Application.Top || top?.SuperView == Application.Top) {
+			if (!(top is Window && top == Application.Top) && (top?.SuperView == null || top == Application.Top || top?.SuperView == Application.Top)) {
 				s = Application.Top.StatusBar?.Visible == true;
 				sb = Application.Top.StatusBar;
-			} else {
+			} else if (!(top is Window && top == Application.Top)) {
 				var t = top.SuperView;
 				while (!(t is Toplevel)) {
 					t = t.SuperView;

+ 6 - 9
Terminal.Gui/Core/View.cs

@@ -371,7 +371,7 @@ namespace Terminal.Gui {
 						SuperView?.EnsureFocus ();
 						if (SuperView != null && SuperView.Focused == null) {
 							SuperView.FocusNext ();
-							if (SuperView.Focused == null) {
+							if (SuperView.Focused == null && Application.Current != null) {
 								Application.Current.FocusNext ();
 							}
 							Application.EnsuresTopOnFront ();
@@ -995,9 +995,6 @@ namespace Terminal.Gui {
 			view.tabIndex = -1;
 			SetNeedsLayout ();
 			SetNeedsDisplay ();
-			if (subviews.Count < 1) {
-				CanFocus = false;
-			}
 			foreach (var v in subviews) {
 				if (v.Frame.IntersectsWith (touched))
 					view.SetNeedsDisplay ();
@@ -2199,7 +2196,7 @@ namespace Terminal.Gui {
 			} else {
 				actY = y?.Anchor (hostFrame.Height) ?? 0;
 
-				actH = Math.Max (CalculateActualHight (height, hostFrame, actY, s), 0);
+				actH = Math.Max (CalculateActualHeight (height, hostFrame, actY, s), 0);
 			}
 
 			var r = new Rect (actX, actY, actW, actH);
@@ -2240,7 +2237,7 @@ namespace Terminal.Gui {
 			return actW;
 		}
 
-		private int CalculateActualHight (Dim height, Rect hostFrame, int actY, Size s)
+		private int CalculateActualHeight (Dim height, Rect hostFrame, int actY, Size s)
 		{
 			int actH;
 			switch (height) {
@@ -2248,8 +2245,8 @@ namespace Terminal.Gui {
 				actH = AutoSize ? s.Height : hostFrame.Height;
 				break;
 			case Dim.DimCombine combine:
-				int leftActH = CalculateActualHight (combine.left, hostFrame, actY, s);
-				int rightActH = CalculateActualHight (combine.right, hostFrame, actY, s);
+				int leftActH = CalculateActualHeight (combine.left, hostFrame, actY, s);
+				int rightActH = CalculateActualHeight (combine.right, hostFrame, actY, s);
 				if (combine.add) {
 					actH = leftActH + rightActH;
 				} else {
@@ -2449,7 +2446,7 @@ namespace Terminal.Gui {
 
 			foreach (var v in ordered) {
 				if (v.LayoutStyle == LayoutStyle.Computed) {
-					v.SetRelativeLayout (Frame);
+					v.SetRelativeLayout (v?.SuperView.Frame ?? Frame);
 				}
 
 				v.LayoutSubviews ();

+ 8 - 4
Terminal.Gui/Core/Window.cs

@@ -88,11 +88,18 @@ namespace Terminal.Gui {
 
 			public ContentView (Rect frame, Window instance) : base (frame)
 			{
-				this.instance = instance;
+				Initialize (instance);
 			}
 			public ContentView (Window instance) : base ()
+			{
+				Initialize (instance);
+			}
+
+			private void Initialize (Window instance)
 			{
 				this.instance = instance;
+				CanFocus = this.instance.CanFocus;
+				Driver?.SetCursorVisibility (CursorVisibility.Invisible);
 			}
 
 			public override void OnCanFocusChanged ()
@@ -263,9 +270,6 @@ namespace Terminal.Gui {
 			SetNeedsDisplay ();
 			contentView.Remove (view);
 
-			if (contentView.InternalSubviews.Count < 1) {
-				CanFocus = false;
-			}
 			RemoveMenuStatusBar (view);
 			if (view != contentView && Focused == null) {
 				FocusFirst ();

+ 49 - 29
Terminal.Gui/Terminal.Gui.csproj

@@ -1,30 +1,48 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
+  <!-- =================================================================== -->
+  <!-- Version numbers -->
+  <!-- Automatically updated by gitversion (run `dotnet-gitversion /updateprojectfiles`)  -->
+  <!-- GitVersion.xml controls settings  -->
+  <!-- =================================================================== -->
+  <PropertyGroup>
+    <FileVersion>1.0.0.0</FileVersion>
+    <Version>1.0.0</Version>
+  </PropertyGroup>
+  <!-- =================================================================== -->
+  <!-- .NET Build Settings -->
+  <!-- =================================================================== -->
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
     <DebugType>portable</DebugType>
+    <VersionSuffix></VersionSuffix>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
     <DefineConstants>TRACE;DEBUG_IDISPOSABLE</DefineConstants>
     <DebugType>portable</DebugType>
   </PropertyGroup>
   <PropertyGroup>
-    <!-- Version numbers are automatically updated by gitversion when a release is released -->
-    <!-- In the source tree the version will always be 1.0 for all projects. -->
-    <!-- Do not modify these. Do NOT commit after manually running `dotnet-gitversion /updateprojectfiles` -->
-    <AssemblyVersion>1.0</AssemblyVersion>
-    <FileVersion>1.0</FileVersion>
-    <Version>1.0</Version>
-    <InformationalVersion>1.0</InformationalVersion>
+    <TargetFrameworks>net472;netstandard2.1;net7.0</TargetFrameworks>
+    <RootNamespace>Terminal.Gui</RootNamespace>
+    <AssemblyName>Terminal.Gui</AssemblyName>
+    <SuppressNETCoreSdkPreviewMessage>true</SuppressNETCoreSdkPreviewMessage>
   </PropertyGroup>
+  <!-- =================================================================== -->
+  <!-- Dependencies -->
+  <!-- =================================================================== -->
   <ItemGroup>
     <PackageReference Include="NStack.Core" Version="1.0.7" />
+    <PackageReference Include="System.Management" Version="7.0.2" />
+    <!-- Enable Nuget Source Link for github -->
+    <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
     <InternalsVisibleTo Include="UnitTests" />
   </ItemGroup>
-  <!-- Uncomment the RestoreSources element to have dotnet restore pull NStack from a local dir for testing -->
   <PropertyGroup>
+    <!-- Uncomment the RestoreSources element to have dotnet restore pull NStack from a local dir for testing -->
     <!-- See https://stackoverflow.com/a/44463578/297526 -->
     <!--<RestoreSources>$(RestoreSources);..\..\NStack\NStack\bin\Debug;https://api.nuget.org/v3/index.json</RestoreSources>-->
   </PropertyGroup>
+  <!-- =================================================================== -->
   <!-- API Documentation -->
+  <!-- =================================================================== -->
   <ItemGroup>
     <None Include="..\docfx\images\logo.png">
       <Pack>True</Pack>
@@ -35,6 +53,9 @@
       <PackagePath>\</PackagePath>
     </None>
   </ItemGroup>
+  <!-- =================================================================== -->
+  <!-- i18 -->
+  <!-- =================================================================== -->
   <ItemGroup>
     <Compile Update="Resources\Strings.Designer.cs">
       <DesignTime>True</DesignTime>
@@ -48,38 +69,37 @@
       <LastGenOutput>Strings.Designer.cs</LastGenOutput>
     </EmbeddedResource>
   </ItemGroup>
-  <!-- Enable Nuget Source Link for github -->
-  <ItemGroup>
-    <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
-    <PackageReference Include="System.Management" Version="7.0.2" />
-  </ItemGroup>
+  <!-- =================================================================== -->
+  <!-- Nuget  -->
+  <!-- =================================================================== -->
   <PropertyGroup>
-    <TargetFrameworks>net472;netstandard2.1;net7.0</TargetFrameworks>
-    <RootNamespace>Terminal.Gui</RootNamespace>
-    <AssemblyName>Terminal.Gui</AssemblyName>
-    <DocumentationFile>bin\Release\Terminal.Gui.xml</DocumentationFile>
-    <GenerateDocumentationFile Condition=" '$(Configuration)' == 'Release' ">true</GenerateDocumentationFile>
-    <!--<GeneratePackageOnBuild Condition=" '$(Configuration)' == 'Release' ">true</GeneratePackageOnBuild>-->
     <PackageId>Terminal.Gui</PackageId>
     <PackageLicenseExpression>MIT</PackageLicenseExpression>
     <PackageProjectUrl>https://github.com/gui-cs/Terminal.Gui/</PackageProjectUrl>
-    <RepositoryUrl>https://github.com/gui-cs/Terminal.Gui.git</RepositoryUrl>
-    <RepositoryType>git</RepositoryType>
-    <IncludeSymbols>true</IncludeSymbols>
-    <SymbolPackageFormat>snupkg</SymbolPackageFormat>
-    <!-- Publish the repository URL in the built .nupkg (in the NuSpec <Repository> element) -->
-    <PublishRepositoryUrl>true</PublishRepositoryUrl>
-    <!-- Embed source files that are not tracked by the source control manager in the PDB -->
-    <EmbedUntrackedSources>true</EmbedUntrackedSources>
     <PackageIcon>logo.png</PackageIcon>
     <PackageReadmeFile>README.md</PackageReadmeFile>
     <PackageTags>csharp, terminal, c#, f#, gui, toolkit, console, tui</PackageTags>
     <Description>Cross platform Terminal UI toolkit for .NET</Description>
-    <Owners>Miguel de Icaza, Charlie Kindel</Owners>
+    <Owners>Miguel de Icaza, Tig Kindel</Owners>
     <Summary>A toolkit for building rich console apps for .NET that works on Windows, Mac, and Linux/Unix.</Summary>
     <Title>Terminal.Gui - Cross platform Terminal User Interface (TUI) toolkit for .NET</Title>
     <PackageReleaseNotes>
       See: https://github.com/gui-cs/Terminal.Gui/releases
     </PackageReleaseNotes>
+    <DocumentationFile>bin\Release\Terminal.Gui.xml</DocumentationFile>
+    <GeneratePackageOnBuild Condition=" '$(Configuration)' == 'Debug' ">true</GeneratePackageOnBuild>
+    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
+    <RepositoryUrl>https://github.com/gui-cs/Terminal.Gui.git</RepositoryUrl>
+    <RepositoryType>git</RepositoryType>
+    <IncludeSymbols>true</IncludeSymbols>
+    <SymbolPackageFormat>snupkg</SymbolPackageFormat>
+    <!-- Publish the repository URL in the built .nupkg (in the NuSpec <Repository> element) -->
+    <PublishRepositoryUrl>true</PublishRepositoryUrl>
+    <!-- Embed source files that are not tracked by the source control manager in the PDB -->
+    <GitRepositoryRemoteName>upstream</GitRepositoryRemoteName>
+    <EmbedUntrackedSources>true</EmbedUntrackedSources>
+    <EnableSourceLink>true</EnableSourceLink>
+    <!--<DebugType>Embedded</DebugType>-->
+    <Authors>Miguel de Icaza, Tig Kindel (@tig), @BDisp</Authors>
   </PropertyGroup>
 </Project>

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

@@ -162,7 +162,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		public void Hide ()
 		{
-			menuBar.CleanUp ();
+			menuBar?.CleanUp ();
 			Dispose ();
 		}
 

+ 64 - 14
Terminal.Gui/Views/Menu.cs

@@ -414,7 +414,7 @@ namespace Terminal.Gui {
 
 				current = -1;
 				for (int i = 0; i < barItems.Children?.Length; i++) {
-					if (barItems.Children [i] != null) {
+					if (barItems.Children [i]?.IsEnabled () == true) {
 						current = i;
 						break;
 					}
@@ -572,17 +572,14 @@ namespace Terminal.Gui {
 
 		public void Run (Action action)
 		{
-			if (action == null)
+			if (action == null || host == null)
 				return;
 
 			Application.UngrabMouse ();
 			host.CloseAllMenus ();
 			Application.Refresh ();
 
-			Application.MainLoop.AddIdle (() => {
-				action ();
-				return false;
-			});
+			host.Run (action);
 		}
 
 		public override bool OnLeave (View view)
@@ -946,6 +943,17 @@ namespace Terminal.Gui {
 		/// </summary>
 		public Key Key { get; set; } = Key.F9;
 
+		///<inheritdoc/>
+		public override bool Visible {
+			get => base.Visible;
+			set {
+				base.Visible = value;
+				if (!value) {
+					CloseAllMenus ();
+				}
+			}
+		}
+
 		/// <summary>
 		/// Initializes a new instance of the <see cref="MenuBar"/>.
 		/// </summary>
@@ -1130,7 +1138,11 @@ namespace Terminal.Gui {
 			Application.UngrabMouse ();
 			CloseAllMenus ();
 			Application.Refresh ();
+			Run (action);
+		}
 
+		internal void Run (Action action)
+		{
 			Application.MainLoop.AddIdle (() => {
 				action ();
 				return false;
@@ -1641,6 +1653,44 @@ namespace Terminal.Gui {
 					if (Char.ToUpperInvariant ((char)mi.Title [p + 1]) == c) {
 						ProcessMenu (i, mi);
 						return true;
+					} else if (mi.Children?.Length > 0) {
+						if (FindAndOpenChildrenMenuByHotkey (kb, mi.Children)) {
+							return true;
+						}
+					}
+				} else if (mi.Children?.Length > 0) {
+					if (FindAndOpenChildrenMenuByHotkey (kb, mi.Children)) {
+						return true;
+					}
+				}
+			}
+
+			return false;
+		}
+
+		bool FindAndOpenChildrenMenuByHotkey (KeyEvent kb, MenuItem [] children)
+		{
+			var c = ((uint)kb.Key & (uint)Key.CharMask);
+			for (int i = 0; i < children.Length; i++) {
+				var mi = children [i];
+				int p = mi.Title.IndexOf (MenuBar.HotKeySpecifier);
+				if (p != -1 && p + 1 < mi.Title.RuneCount) {
+					if (Char.ToUpperInvariant ((char)mi.Title [p + 1]) == c) {
+						if (mi.IsEnabled ()) {
+							var action = mi.Action;
+							if (action != null) {
+								Run (action);
+							}
+						}
+						return true;
+					} else if (mi is MenuBarItem menuBarItem && menuBarItem?.Children.Length > 0) {
+						if (FindAndOpenChildrenMenuByHotkey (kb, menuBarItem.Children)) {
+							return true;
+						}
+					}
+				} else if (mi is MenuBarItem menuBarItem && menuBarItem?.Children.Length > 0) {
+					if (FindAndOpenChildrenMenuByHotkey (kb, menuBarItem.Children)) {
+						return true;
 					}
 				}
 			}
@@ -1662,12 +1712,11 @@ namespace Terminal.Gui {
 					continue;
 				}
 				if ((!(mi is MenuBarItem mbiTopLevel) || mbiTopLevel.IsTopLevel) && mi.Shortcut != Key.Null && mi.Shortcut == (Key)key) {
-					var action = mi.Action;
-					if (action != null) {
-						Application.MainLoop.AddIdle (() => {
-							action ();
-							return false;
-						});
+					if (mi.IsEnabled ()) {
+						var action = mi.Action;
+						if (action != null) {
+							Run (action);
+						}
 					}
 					return true;
 				}
@@ -1707,10 +1756,11 @@ namespace Terminal.Gui {
 		public override bool ProcessHotKey (KeyEvent kb)
 		{
 			if (kb.Key == Key) {
-				if (!IsMenuOpen)
+				if (Visible && !IsMenuOpen) {
 					OpenMenu ();
-				else
+				} else {
 					CloseAllMenus ();
+				}
 				return true;
 			}
 

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

@@ -109,7 +109,7 @@ namespace Terminal.Gui {
 				OtherScrollBarView.ShowScrollIndicator = true;
 			}
 			ShowScrollIndicator = true;
-			contentBottomRightCorner = new View (" ") { Visible = host.Visible };
+			contentBottomRightCorner = new View (" ") { Visible = host.Visible, ColorScheme = host.ColorScheme };
 			Host.SuperView.Add (contentBottomRightCorner);
 			contentBottomRightCorner.X = Pos.Right (host) - 1;
 			contentBottomRightCorner.Y = Pos.Bottom (host) - 1;
@@ -346,23 +346,23 @@ namespace Terminal.Gui {
 			}
 
 			if (showBothScrollIndicator) {
-				if (contentBottomRightCorner != null) {
+				if (contentBottomRightCorner != null && !contentBottomRightCorner.Visible) {
 					contentBottomRightCorner.Visible = true;
-				} else if (otherScrollBarView != null && otherScrollBarView.contentBottomRightCorner != null) {
+				} else if (otherScrollBarView != null && otherScrollBarView.contentBottomRightCorner != null && !otherScrollBarView.contentBottomRightCorner.Visible) {
 					otherScrollBarView.contentBottomRightCorner.Visible = true;
 				}
 			} else if (!showScrollIndicator) {
-				if (contentBottomRightCorner != null) {
+				if (contentBottomRightCorner != null && contentBottomRightCorner.Visible) {
 					contentBottomRightCorner.Visible = false;
-				} else if (otherScrollBarView != null && otherScrollBarView.contentBottomRightCorner != null) {
+				} else if (otherScrollBarView != null && otherScrollBarView.contentBottomRightCorner != null && otherScrollBarView.contentBottomRightCorner.Visible) {
 					otherScrollBarView.contentBottomRightCorner.Visible = false;
 				}
 				if (Application.MouseGrabView != null && Application.MouseGrabView == this) {
 					Application.UngrabMouse ();
 				}
-			} else if (contentBottomRightCorner != null) {
+			} else if (contentBottomRightCorner != null && contentBottomRightCorner.Visible) {
 				contentBottomRightCorner.Visible = false;
-			} else if (otherScrollBarView != null && otherScrollBarView.contentBottomRightCorner != null) {
+			} else if (otherScrollBarView != null && otherScrollBarView.contentBottomRightCorner != null && otherScrollBarView.contentBottomRightCorner.Visible) {
 				otherScrollBarView.contentBottomRightCorner.Visible = false;
 			}
 			if (Host?.Visible == true && showScrollIndicator && !Visible) {

+ 41 - 5
Terminal.Gui/Views/StatusBar.cs

@@ -27,11 +27,13 @@ namespace Terminal.Gui {
 		/// <param name="shortcut">Shortcut to activate the <see cref="StatusItem"/>.</param>
 		/// <param name="title">Title for the <see cref="StatusItem"/>.</param>
 		/// <param name="action">Action to invoke when the <see cref="StatusItem"/> is activated.</param>
-		public StatusItem (Key shortcut, ustring title, Action action)
+		/// <param name="canExecute">Function to determine if the action can currently be executed.</param>
+		public StatusItem (Key shortcut, ustring title, Action action, Func<bool> canExecute = null)
 		{
 			Title = title ?? "";
 			Shortcut = shortcut;
 			Action = action;
+			CanExecute = canExecute;
 		}
 
 		/// <summary>
@@ -54,7 +56,22 @@ namespace Terminal.Gui {
 		/// Gets or sets the action to be invoked when the statusbar item is triggered
 		/// </summary>
 		/// <value>Action to invoke.</value>
-		public Action Action { get; }
+		public Action Action { get; set; }
+
+		/// <summary>
+		/// Gets or sets the action to be invoked to determine if the <see cref="StatusItem"/> can be triggered. 
+		/// If <see cref="CanExecute"/> returns <see langword="true"/> the status item will be enabled. Otherwise, it will be disabled.
+		/// </summary>
+		/// <value>Function to determine if the action is can be executed or not.</value>
+		public Func<bool> CanExecute { get; set; }
+
+		/// <summary>
+		/// Returns <see langword="true"/> if the status item is enabled. This method is a wrapper around <see cref="CanExecute"/>.
+		/// </summary>
+		public bool IsEnabled ()
+		{
+			return CanExecute == null ? true : CanExecute ();
+		}
 
 		/// <summary>
 		/// Gets or sets arbitrary data for the status item.
@@ -116,6 +133,17 @@ namespace Terminal.Gui {
 			return result;
 		}
 
+		Attribute DetermineColorSchemeFor (StatusItem item)
+		{
+			if (item != null) {
+				if (item.IsEnabled ()) {
+					return GetNormalColor ();
+				}
+				return ColorScheme.Disabled;
+			}
+			return GetNormalColor ();
+		}
+
 		///<inheritdoc/>
 		public override void Redraw (Rect bounds)
 		{
@@ -129,9 +157,12 @@ namespace Terminal.Gui {
 			Driver.SetAttribute (scheme);
 			for (int i = 0; i < Items.Length; i++) {
 				var title = Items [i].Title.ToString ();
+				Driver.SetAttribute (DetermineColorSchemeFor (Items [i]));
 				for (int n = 0; n < Items [i].Title.RuneCount; n++) {
 					if (title [n] == '~') {
-						scheme = ToggleScheme (scheme);
+						if (Items [i].IsEnabled ()) {
+							scheme = ToggleScheme (scheme);
+						}
 						continue;
 					}
 					Driver.AddRune (title [n]);
@@ -149,7 +180,9 @@ namespace Terminal.Gui {
 		{
 			foreach (var item in Items) {
 				if (kb.Key == item.Shortcut) {
-					Run (item.Action);
+					if (item.IsEnabled ()) {
+						Run (item.Action);
+					}
 					return true;
 				}
 			}
@@ -165,7 +198,10 @@ namespace Terminal.Gui {
 			int pos = 1;
 			for (int i = 0; i < Items.Length; i++) {
 				if (me.X >= pos && me.X < pos + GetItemTitleLength (Items [i].Title)) {
-					Run (Items [i].Action);
+					var item = Items [i];
+					if (item.IsEnabled ()) {
+						Run (item.Action);
+					}
 					break;
 				}
 				pos += GetItemTitleLength (Items [i].Title) + 3;

+ 37 - 8
Terminal.Gui/Views/TextView.cs

@@ -1654,11 +1654,8 @@ namespace Terminal.Gui {
 		public int BottomOffset {
 			get => bottomOffset;
 			set {
-				if (currentRow == Lines - 1 && bottomOffset > 0 && value == 0) {
-					topRow = Math.Max (topRow - bottomOffset, 0);
-				}
+				topRow = AdjustOffset (value);
 				bottomOffset = value;
-				Adjust ();
 			}
 		}
 
@@ -1669,11 +1666,8 @@ namespace Terminal.Gui {
 		public int RightOffset {
 			get => rightOffset;
 			set {
-				if (!wordWrap && currentColumn == GetCurrentLine ().Count && rightOffset > 0 && value == 0) {
-					leftColumn = Math.Max (leftColumn - rightOffset, 0);
-				}
+				leftColumn = AdjustOffset (value, false);
 				rightOffset = value;
-				Adjust ();
 			}
 		}
 
@@ -2645,6 +2639,7 @@ namespace Terminal.Gui {
 				HistoryText.LineStatus.Replaced);
 
 			UpdateWrapModel ();
+			OnContentsChanged ();
 		}
 
 		// The column we are tracking, or -1 if we are not tracking any column
@@ -2710,6 +2705,30 @@ namespace Terminal.Gui {
 			OnUnwrappedCursorPosition ();
 		}
 
+		int AdjustOffset (int valueOffset, bool isRow = true)
+		{
+			var curWrap = isRow ? false : wordWrap;
+			var curLength = isRow ? Lines - 1 : GetCurrentLine ().Count;
+			var curStart = isRow ? topRow : leftColumn;
+			var curOffset = isRow ? bottomOffset : rightOffset;
+			var curSize = isRow ? Frame.Height - valueOffset : Frame.Width - valueOffset;
+			var newStart = curStart;
+
+			if (!curWrap) {
+				if (curStart > 0 && curOffset > 0 && valueOffset == 0) {
+					newStart = Math.Max (curStart - curOffset, 0);
+				} else if (curStart > 0 && curOffset == 0 && valueOffset > 0) {
+					newStart = Math.Max (Math.Min (curStart + valueOffset, curLength - curSize + 1), 0);
+				}
+
+				if (newStart != curStart) {
+					Application.MainLoop.Invoke (() => SetNeedsDisplay ());
+				}
+			}
+
+			return newStart;
+		}
+
 		/// <summary>
 		/// Event arguments for events for when the contents of the TextView change. E.g. the <see cref="ContentsChanged"/> event.
 		/// </summary>
@@ -3550,6 +3569,7 @@ namespace Terminal.Gui {
 			}
 			if (DeleteTextForwards ()) {
 				UpdateWrapModel ();
+				OnContentsChanged ();
 
 				return;
 			}
@@ -3557,6 +3577,7 @@ namespace Terminal.Gui {
 			UpdateWrapModel ();
 
 			DoNeededAction ();
+			OnContentsChanged ();
 		}
 
 		/// <summary>
@@ -3581,11 +3602,13 @@ namespace Terminal.Gui {
 					HistoryText.LineStatus.Replaced);
 
 				UpdateWrapModel ();
+				OnContentsChanged ();
 
 				return;
 			}
 			if (DeleteTextBackwards ()) {
 				UpdateWrapModel ();
+				OnContentsChanged ();
 
 				return;
 			}
@@ -3593,6 +3616,7 @@ namespace Terminal.Gui {
 			UpdateWrapModel ();
 
 			DoNeededAction ();
+			OnContentsChanged ();
 		}
 
 		void MoveLeft ()
@@ -3972,6 +3996,8 @@ namespace Terminal.Gui {
 
 				historyText.Add (new List<List<Rune>> () { new List<Rune> (GetCurrentLine ()) }, CursorPosition,
 					HistoryText.LineStatus.Replaced);
+
+				SetNeedsDisplay ();
 				OnContentsChanged ();
 			} else {
 				if (selecting) {
@@ -3984,6 +4010,8 @@ namespace Terminal.Gui {
 					historyText.ReplaceLast (new List<List<Rune>> () { new List<Rune> (GetCurrentLine ()) }, CursorPosition,
 						HistoryText.LineStatus.Original);
 				}
+
+				SetNeedsDisplay ();
 			}
 			UpdateWrapModel ();
 			selecting = false;
@@ -4095,6 +4123,7 @@ namespace Terminal.Gui {
 			currentColumn = line.Count;
 			TrackColumn ();
 			PositionCursor ();
+			SetNeedsDisplay ();
 		}
 
 		/// <summary>

+ 20 - 18
Terminal.sln

@@ -15,9 +15,13 @@ EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E143FB1F-0B88-48CB-9086-72CDCECFCD22}"
 	ProjectSection(SolutionItems) = preProject
 		.gitignore = .gitignore
+		.github\CODEOWNERS = .github\CODEOWNERS
 		CODE_OF_CONDUCT.md = CODE_OF_CONDUCT.md
 		CONTRIBUTING.md = CONTRIBUTING.md
 		.github\workflows\dotnet-core.yml = .github\workflows\dotnet-core.yml
+		GitVersion.yml = GitVersion.yml
+		global.json = global.json
+		nuget.config = nuget.config
 		.github\workflows\publish.yml = .github\workflows\publish.yml
 		README.md = README.md
 		testenvironments.json = testenvironments.json
@@ -31,23 +35,6 @@ Global
 		Release|x86 = Release|x86
 	EndGlobalSection
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
-		{88979F89-9A42-448F-AE3E-3060145F6375}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{88979F89-9A42-448F-AE3E-3060145F6375}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{88979F89-9A42-448F-AE3E-3060145F6375}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{88979F89-9A42-448F-AE3E-3060145F6375}.Debug|x86.Build.0 = Debug|Any CPU
-		{88979F89-9A42-448F-AE3E-3060145F6375}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{88979F89-9A42-448F-AE3E-3060145F6375}.Release|Any CPU.Build.0 = Release|Any CPU
-		{88979F89-9A42-448F-AE3E-3060145F6375}.Release|x86.ActiveCfg = Release|Any CPU
-		{88979F89-9A42-448F-AE3E-3060145F6375}.Release|x86.Build.0 = Release|Any CPU
-
-		{B0A602CD-E176-449D-8663-64238D54F857}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{B0A602CD-E176-449D-8663-64238D54F857}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{B0A602CD-E176-449D-8663-64238D54F857}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{B0A602CD-E176-449D-8663-64238D54F857}.Debug|x86.Build.0 = Debug|Any CPU
-		{B0A602CD-E176-449D-8663-64238D54F857}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{B0A602CD-E176-449D-8663-64238D54F857}.Release|Any CPU.Build.0 = Release|Any CPU
-		{B0A602CD-E176-449D-8663-64238D54F857}.Release|x86.ActiveCfg = Release|Any CPU
-		{B0A602CD-E176-449D-8663-64238D54F857}.Release|x86.Build.0 = Release|Any CPU
 		{00F366F8-DEE4-482C-B9FD-6DB0200B79E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{00F366F8-DEE4-482C-B9FD-6DB0200B79E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{00F366F8-DEE4-482C-B9FD-6DB0200B79E5}.Debug|x86.ActiveCfg = Debug|Any CPU
@@ -56,7 +43,14 @@ Global
 		{00F366F8-DEE4-482C-B9FD-6DB0200B79E5}.Release|Any CPU.Build.0 = Release|Any CPU
 		{00F366F8-DEE4-482C-B9FD-6DB0200B79E5}.Release|x86.ActiveCfg = Release|Any CPU
 		{00F366F8-DEE4-482C-B9FD-6DB0200B79E5}.Release|x86.Build.0 = Release|Any CPU
-
+		{88979F89-9A42-448F-AE3E-3060145F6375}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{88979F89-9A42-448F-AE3E-3060145F6375}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{88979F89-9A42-448F-AE3E-3060145F6375}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{88979F89-9A42-448F-AE3E-3060145F6375}.Debug|x86.Build.0 = Debug|Any CPU
+		{88979F89-9A42-448F-AE3E-3060145F6375}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{88979F89-9A42-448F-AE3E-3060145F6375}.Release|Any CPU.Build.0 = Release|Any CPU
+		{88979F89-9A42-448F-AE3E-3060145F6375}.Release|x86.ActiveCfg = Release|Any CPU
+		{88979F89-9A42-448F-AE3E-3060145F6375}.Release|x86.Build.0 = Release|Any CPU
 		{8B901EDE-8974-4820-B100-5226917E2990}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{8B901EDE-8974-4820-B100-5226917E2990}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{8B901EDE-8974-4820-B100-5226917E2990}.Debug|x86.ActiveCfg = Debug|Any CPU
@@ -73,6 +67,14 @@ Global
 		{44E15B48-0DB2-4560-82BD-D3B7989811C3}.Release|Any CPU.Build.0 = Release|Any CPU
 		{44E15B48-0DB2-4560-82BD-D3B7989811C3}.Release|x86.ActiveCfg = Release|Any CPU
 		{44E15B48-0DB2-4560-82BD-D3B7989811C3}.Release|x86.Build.0 = Release|Any CPU
+		{B0A602CD-E176-449D-8663-64238D54F857}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{B0A602CD-E176-449D-8663-64238D54F857}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{B0A602CD-E176-449D-8663-64238D54F857}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{B0A602CD-E176-449D-8663-64238D54F857}.Debug|x86.Build.0 = Debug|Any CPU
+		{B0A602CD-E176-449D-8663-64238D54F857}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{B0A602CD-E176-449D-8663-64238D54F857}.Release|Any CPU.Build.0 = Release|Any CPU
+		{B0A602CD-E176-449D-8663-64238D54F857}.Release|x86.ActiveCfg = Release|Any CPU
+		{B0A602CD-E176-449D-8663-64238D54F857}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 6 - 1
UICatalog/Properties/launchSettings.json

@@ -52,7 +52,12 @@
       "commandLineArgs": "\"Windows & FrameViews\""
     },
     "Docker": {
-      "commandName": "Docker"      
+      "commandName": "Docker"
+    },
+    "UICatalog WT": {
+      "commandName": "Executable",
+      "executablePath": "wt",
+      "commandLineArgs": "UICatalog.exe"
     }
   }
 }

+ 0 - 22
UICatalog/UICatalog.cs

@@ -121,8 +121,6 @@ namespace UICatalog {
 			// a Scenario was selected. Otherwise, the user wants to exit UI Catalog.
 			Application.Init ();
 			
-			Application.EnableConsoleScrolling = _enableConsoleScrolling;
-			
 			Application.Run<UICatalogTopLevel> ();
 			Application.Shutdown ();
 
@@ -144,7 +142,6 @@ namespace UICatalog {
 
 		static bool _useSystemConsole = false;
 		static ConsoleDriver.DiagnosticFlags _diagnosticFlags;
-		static bool _enableConsoleScrolling = false;
 		static bool _isFirstRunning = true;
 		static ColorScheme _colorScheme;
 
@@ -154,7 +151,6 @@ namespace UICatalog {
 		/// </summary>
 		class UICatalogTopLevel : Toplevel {
 			public MenuItem miIsMouseDisabled;
-			public MenuItem miEnableConsoleScrolling;
 
 			public FrameView LeftPane;
 			public ListView CategoryListView;
@@ -282,7 +278,6 @@ namespace UICatalog {
 				}
 
 				miIsMouseDisabled.Checked = Application.IsMouseDisabled;
-				miEnableConsoleScrolling.Checked = Application.EnableConsoleScrolling;
 				DriverName.Title = $"Driver: {Driver.GetType ().Name}";
 				OS.Title = $"OS: {Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.OperatingSystem} {Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.OperatingSystemVersion}";
 
@@ -319,7 +314,6 @@ namespace UICatalog {
 				List<MenuItem []> menuItems = new List<MenuItem []> ();
 				menuItems.Add (CreateDiagnosticFlagsMenuItems ());
 				menuItems.Add (new MenuItem [] { null });
-				menuItems.Add (CreateEnableConsoleScrollingMenuItems ());
 				menuItems.Add (CreateDisabledEnabledMouseItems ());
 				menuItems.Add (CreateKeybindingsMenuItems ());
 				return menuItems;
@@ -357,22 +351,6 @@ namespace UICatalog {
 				return menuItems.ToArray ();
 			}
 
-			MenuItem [] CreateEnableConsoleScrollingMenuItems ()
-			{
-				List<MenuItem> menuItems = new List<MenuItem> ();
-				miEnableConsoleScrolling = new MenuItem ();
-				miEnableConsoleScrolling.Title = "_Enable Console Scrolling";
-				miEnableConsoleScrolling.Shortcut = Key.CtrlMask | Key.AltMask | (Key)miEnableConsoleScrolling.Title.ToString ().Substring (1, 1) [0];
-				miEnableConsoleScrolling.CheckType |= MenuItemCheckStyle.Checked;
-				miEnableConsoleScrolling.Action += () => {
-					miEnableConsoleScrolling.Checked = !miEnableConsoleScrolling.Checked;
-					Application.EnableConsoleScrolling = miEnableConsoleScrolling.Checked;
-				};
-				menuItems.Add (miEnableConsoleScrolling);
-
-				return menuItems.ToArray ();
-			}
-
 			MenuItem [] CreateDiagnosticFlagsMenuItems ()
 			{
 				const string OFF = "Diagnostics: _Off";

+ 6 - 9
UICatalog/UICatalog.csproj

@@ -2,16 +2,13 @@
   <PropertyGroup>
     <OutputType>Exe</OutputType>
     <TargetFramework>net7.0</TargetFramework>
-    <LangVersion>8.0</LangVersion>
+    <LangVersion>9.0</LangVersion>
     <StartupObject>UICatalog.UICatalogApp</StartupObject>
+    <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
     <!-- Version numbers are automatically updated by gitversion when a release is released -->
-    <!-- In the source tree the version will always be 2.0 for all projects. -->
     <!-- Do not modify these. -->
-    <AssemblyVersion>1.10.1.0</AssemblyVersion>
-    <FileVersion>1.10.1.0</FileVersion>
-    <Version>1.10.1</Version>
-    <InformationalVersion>1.10.1+6.Branch.main.Sha.f7ee66ddbf8dbcfb0d96af7d63789879091670ec</InformationalVersion>
-    <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
+    <FileVersion>1.0.0.0</FileVersion>
+    <Version>1.0.0</Version>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
     <DefineConstants>TRACE</DefineConstants>
@@ -23,8 +20,8 @@
     <None Update="./Scenarios/Spinning_globe_dark_small.gif" CopyToOutputDirectory="PreserveNewest" />
   </ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.18.1" />
-    <PackageReference Include="SixLabors.ImageSharp" Version="3.0.1" />
+    <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.5" />
+    <PackageReference Include="SixLabors.ImageSharp" Version="3.0.2" />
     <PackageReference Include="CsvHelper" Version="30.0.1" />
     <PackageReference Include="Microsoft.DotNet.PlatformAbstractions" Version="3.1.6" />
   </ItemGroup>

+ 0 - 2
UnitTests/Application/ApplicationTests.cs

@@ -24,7 +24,6 @@ namespace Terminal.Gui.ApplicationTests {
 			Assert.Null (Application.Driver);
 			Assert.Null (Application.Top);
 			Assert.Null (Application.Current);
-			Assert.False (Application.EnableConsoleScrolling);
 			Assert.Null (Application.MainLoop);
 			Assert.Null (Application.Iteration);
 			Assert.Null (Application.RootMouseEvent);
@@ -36,7 +35,6 @@ namespace Terminal.Gui.ApplicationTests {
 			Assert.NotNull (Application.Driver);
 			Assert.NotNull (Application.Top);
 			Assert.NotNull (Application.Current);
-			Assert.False (Application.EnableConsoleScrolling);
 			Assert.NotNull (Application.MainLoop);
 			Assert.Null (Application.Iteration);
 			Assert.Null (Application.RootMouseEvent);

+ 0 - 9
UnitTests/AssemblyInfo.cs

@@ -1,9 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.Reflection;
-using Terminal.Gui;
-using Xunit;
-
-// Since Application is a singleton we can't run tests in parallel
-[assembly: CollectionBehavior (DisableTestParallelization = true)]
-

+ 24 - 0
UnitTests/Core/BorderTests.cs

@@ -619,5 +619,29 @@ At 0,0
 ████████████████████
 At 0,4              ", output);
 		}
+
+		[Fact]
+		public void BorderBrush_Background_Only_Is_Set_To_Valid_Color_Enum ()
+		{
+			var border = new Border ();
+			Assert.Equal ((Color)(-1), border.BorderBrush);
+			Assert.Equal ((Color)(-1), border.Background);
+			Assert.Null (border.GetFieldValue<string> ("borderBrush"));
+			Assert.Null (border.GetFieldValue<string> ("background"));
+
+			border.BorderBrush = (Color)(-1);
+			border.Background = (Color)(-1);
+			Assert.Equal ((Color)(-1), border.BorderBrush);
+			Assert.Equal ((Color)(-1), border.Background);
+			Assert.Null (border.GetFieldValue<Color?> ("borderBrush"));
+			Assert.Null (border.GetFieldValue<Color?> ("background"));
+
+			border.BorderBrush = Color.Blue;
+			border.Background = Color.White;
+			Assert.Equal (Color.Blue, border.BorderBrush);
+			Assert.Equal (Color.White, border.Background);
+			Assert.Equal (Color.Blue, border.GetFieldValue<Color> ("borderBrush"));
+			Assert.Equal (Color.White, border.GetFieldValue<Color?> ("background"));
+		}
 	}
 }

+ 32 - 160
UnitTests/Drivers/ConsoleDriverTests.cs

@@ -169,29 +169,16 @@ namespace Terminal.Gui.DriverTests {
 			Assert.Equal (40, Application.Driver.Rows);
 			Assert.True (wasTerminalResized);
 
-			// MockDriver will still be 120x40
-			wasTerminalResized = false;
-			Application.EnableConsoleScrolling = true;
-			driver.SetWindowSize (40, 20);
-			Assert.Equal (120, Application.Driver.Cols);
-			Assert.Equal (40, Application.Driver.Rows);
-			Assert.Equal (120, Console.BufferWidth);
-			Assert.Equal (40, Console.BufferHeight);
-			Assert.Equal (40, Console.WindowWidth);
-			Assert.Equal (20, Console.WindowHeight);
-			Assert.True (wasTerminalResized);
-
 			Application.Shutdown ();
 		}
 
 		[Theory]
 		[InlineData (typeof (FakeDriver))]
-		public void EnableConsoleScrolling_Is_False_Left_And_Top_Is_Always_Zero (Type driverType)
+		public void Left_And_Top_Is_Always_Zero (Type driverType)
 		{
 			var driver = (FakeDriver)Activator.CreateInstance (driverType);
 			Application.Init (driver);
 
-			Assert.False (Application.EnableConsoleScrolling);
 			Assert.Equal (0, Console.WindowLeft);
 			Assert.Equal (0, Console.WindowTop);
 
@@ -202,124 +189,7 @@ namespace Terminal.Gui.DriverTests {
 			Application.Shutdown ();
 		}
 
-		[Theory]
-		[InlineData (typeof (FakeDriver))]
-		public void EnableConsoleScrolling_Is_True_Left_Cannot_Be_Greater_Than_WindowWidth (Type driverType)
-		{
-			var driver = (FakeDriver)Activator.CreateInstance (driverType);
-			Application.Init (driver);
-
-			Application.EnableConsoleScrolling = true;
-			Assert.True (Application.EnableConsoleScrolling);
-
-			driver.SetWindowPosition (81, 25);
-			Assert.Equal (0, Console.WindowLeft);
-			Assert.Equal (0, Console.WindowTop);
-
-			Application.Shutdown ();
-		}
-
-		[Theory]
-		[InlineData (typeof (FakeDriver))]
-		public void EnableConsoleScrolling_Is_True_Left_Cannot_Be_Greater_Than_BufferWidth_Minus_WindowWidth (Type driverType)
-		{
-			var driver = (FakeDriver)Activator.CreateInstance (driverType);
-			Application.Init (driver);
-
-			Application.EnableConsoleScrolling = true;
-			Assert.True (Application.EnableConsoleScrolling);
-
-			driver.SetWindowPosition (81, 25);
-			Assert.Equal (0, Console.WindowLeft);
-			Assert.Equal (0, Console.WindowTop);
-
-			// MockDriver will now be sets to 120x25
-			driver.SetBufferSize (120, 25);
-			Assert.Equal (120, Application.Driver.Cols);
-			Assert.Equal (25, Application.Driver.Rows);
-			Assert.Equal (120, Console.BufferWidth);
-			Assert.Equal (25, Console.BufferHeight);
-			Assert.Equal (80, Console.WindowWidth);
-			Assert.Equal (25, Console.WindowHeight);
-			driver.SetWindowPosition (121, 25);
-			Assert.Equal (40, Console.WindowLeft);
-			Assert.Equal (0, Console.WindowTop);
-
-			driver.SetWindowSize (90, 25);
-			Assert.Equal (120, Application.Driver.Cols);
-			Assert.Equal (25, Application.Driver.Rows);
-			Assert.Equal (120, Console.BufferWidth);
-			Assert.Equal (25, Console.BufferHeight);
-			Assert.Equal (90, Console.WindowWidth);
-			Assert.Equal (25, Console.WindowHeight);
-			driver.SetWindowPosition (121, 25);
-			Assert.Equal (30, Console.WindowLeft);
-			Assert.Equal (0, Console.WindowTop);
-
-			Application.Shutdown ();
-		}
-
-		[Theory]
-		[InlineData (typeof (FakeDriver))]
-		public void EnableConsoleScrolling_Is_True_Top_Cannot_Be_Greater_Than_WindowHeight (Type driverType)
-		{
-			var driver = (FakeDriver)Activator.CreateInstance (driverType);
-			Application.Init (driver);
-
-			Application.EnableConsoleScrolling = true;
-			Assert.True (Application.EnableConsoleScrolling);
-
-			driver.SetWindowPosition (80, 26);
-			Assert.Equal (0, Console.WindowLeft);
-			Assert.Equal (0, Console.WindowTop);
-
-			Application.Shutdown ();
-		}
-
-		[Theory]
-		[InlineData (typeof (FakeDriver))]
-		public void EnableConsoleScrolling_Is_True_Top_Cannot_Be_Greater_Than_BufferHeight_Minus_WindowHeight (Type driverType)
-		{
-			var driver = (FakeDriver)Activator.CreateInstance (driverType);
-			Application.Init (driver);
-
-			Application.EnableConsoleScrolling = true;
-			Assert.True (Application.EnableConsoleScrolling);
 
-			driver.SetWindowPosition (80, 26);
-			Assert.Equal (0, Console.WindowLeft);
-			Assert.Equal (0, Console.WindowTop);
-
-			// MockDriver will now be sets to 80x40
-			driver.SetBufferSize (80, 40);
-			Assert.Equal (80, Application.Driver.Cols);
-			Assert.Equal (40, Application.Driver.Rows);
-			Assert.Equal (80, Console.BufferWidth);
-			Assert.Equal (40, Console.BufferHeight);
-			Assert.Equal (80, Console.WindowWidth);
-			Assert.Equal (25, Console.WindowHeight);
-			Assert.Equal (0, Console.WindowLeft);
-			Assert.Equal (0, Console.WindowTop);
-			driver.SetWindowPosition (80, 40);
-			Assert.Equal (0, Console.WindowLeft);
-			Assert.Equal (15, Console.WindowTop);
-
-			driver.SetWindowSize (80, 20);
-			Assert.Equal (80, Application.Driver.Cols);
-			Assert.Equal (40, Application.Driver.Rows);
-			Assert.Equal (80, Console.BufferWidth);
-			Assert.Equal (40, Console.BufferHeight);
-			Assert.Equal (80, Console.WindowWidth);
-			Assert.Equal (20, Console.WindowHeight);
-			Assert.Equal (0, Console.WindowLeft);
-			Assert.Equal (15, Console.WindowTop);
-			driver.SetWindowPosition (80, 41);
-			Assert.Equal (0, Console.WindowLeft);
-			Assert.Equal (20, Console.WindowTop);
-
-			Application.Shutdown ();
-		}
-		
 		[Fact, AutoInitShutdown]
 		public void AddRune_On_Clip_Left_Or_Right_Replace_Previous_Or_Next_Wide_Rune_With_Space ()
 		{
@@ -441,7 +311,7 @@ namespace Terminal.Gui.DriverTests {
 		}
 
 		private static object packetLock = new object ();
-		
+
 		/// <summary>
 		/// Sometimes when using remote tools EventKeyRecord sends 'virtual keystrokes'.
 		/// These are indicated with the wVirtualKeyCode of 231. When we see this code
@@ -449,46 +319,48 @@ namespace Terminal.Gui.DriverTests {
 		/// when telling the rest of the framework what button was pressed. For full details
 		/// see: https://github.com/gui-cs/Terminal.Gui/issues/2008
 		/// </summary>
-		[Theory, AutoInitShutdown]
+		[Theory]
 		[ClassData (typeof (PacketTest))]
 		public void TestVKPacket (uint unicodeCharacter, bool shift, bool alt, bool control, uint initialVirtualKey, uint initialScanCode, Key expectedRemapping, uint expectedVirtualKey, uint expectedScanCode)
 		{
-			var modifiers = new ConsoleModifiers ();
-			if (shift) modifiers |= ConsoleModifiers.Shift;
-			if (alt) modifiers |= ConsoleModifiers.Alt;
-			if (control) modifiers |= ConsoleModifiers.Control;
-			var mappedConsoleKey = ConsoleKeyMapping.GetConsoleKeyFromKey (unicodeCharacter, modifiers, out uint scanCode, out uint outputChar);
+			lock (packetLock) {
+				Application.ForceFakeConsole = true;
+				Application.Init ();
 
-			if ((scanCode > 0 || mappedConsoleKey == 0) && mappedConsoleKey == initialVirtualKey) Assert.Equal (mappedConsoleKey, initialVirtualKey);
-			else Assert.Equal (mappedConsoleKey, outputChar < 0xff ? outputChar & 0xff | 0xff << 8 : outputChar);
-			Assert.Equal (scanCode, initialScanCode);
+				var modifiers = new ConsoleModifiers ();
+				if (shift) modifiers |= ConsoleModifiers.Shift;
+				if (alt) modifiers |= ConsoleModifiers.Alt;
+				if (control) modifiers |= ConsoleModifiers.Control;
+				var mappedConsoleKey = ConsoleKeyMapping.GetConsoleKeyFromKey (unicodeCharacter, modifiers, out uint scanCode, out uint outputChar);
 
-			var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (mappedConsoleKey, modifiers, out uint consoleKey, out scanCode);
+				if ((scanCode > 0 || mappedConsoleKey == 0) && mappedConsoleKey == initialVirtualKey) Assert.Equal (mappedConsoleKey, initialVirtualKey);
+				else Assert.Equal (mappedConsoleKey, outputChar < 0xff ? outputChar & 0xff | 0xff << 8 : outputChar);
+				Assert.Equal (scanCode, initialScanCode);
 
-			//if (scanCode > 0 && consoleKey == keyChar && consoleKey > 48 && consoleKey > 57 && consoleKey < 65 && consoleKey > 91) {
-			if (scanCode > 0 && keyChar == 0 && consoleKey == mappedConsoleKey) Assert.Equal (0, (double)keyChar);
-			else Assert.Equal (keyChar, unicodeCharacter);
-			Assert.Equal (consoleKey, expectedVirtualKey);
-			Assert.Equal (scanCode, expectedScanCode);
+				var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (mappedConsoleKey, modifiers, out uint consoleKey, out scanCode);
 
-			var top = Application.Top;
+				//if (scanCode > 0 && consoleKey == keyChar && consoleKey > 48 && consoleKey > 57 && consoleKey < 65 && consoleKey > 91) {
+				if (scanCode > 0 && keyChar == 0 && consoleKey == mappedConsoleKey) Assert.Equal (0, (double)keyChar);
+				else Assert.Equal (keyChar, unicodeCharacter);
+				Assert.Equal (consoleKey, expectedVirtualKey);
+				Assert.Equal (scanCode, expectedScanCode);
 
-			top.KeyPress += (e) => {
-				var after = ShortcutHelper.GetModifiersKey (e.KeyEvent);
-				Assert.Equal (expectedRemapping, after);
-				e.Handled = true;
-				Application.RequestStop ();
-			};
+				var top = Application.Top;
 
-			var iterations = -1;
+				top.KeyPress += (e) => {
+					var after = ShortcutHelper.GetModifiersKey (e.KeyEvent);
+					Assert.Equal (expectedRemapping, after);
+					e.Handled = true;
+					Application.RequestStop ();
+				};
 
-			Application.Iteration += () => {
-				iterations++;
-				if (iterations == 0) Application.Driver.SendKeys ((char)mappedConsoleKey, ConsoleKey.Packet, shift, alt, control);
-			};
+				var iterations = -1;
 
+				Application.Iteration += () => {
+					iterations++;
+					if (iterations == 0) Application.Driver.SendKeys ((char)mappedConsoleKey, ConsoleKey.Packet, shift, alt, control);
+				};
 
-			lock (packetLock) {
 				Application.Run ();
 				Application.Shutdown ();
 			}

+ 69 - 4
UnitTests/Menus/ContextMenuTests.cs

@@ -49,16 +49,21 @@ namespace Terminal.Gui.MenuTests {
 			Assert.NotNull (cm.Host);
 		}
 
-		[Fact]
-		[AutoInitShutdown]
-		public void Show_Hide_IsShow ()
+		private ContextMenu Create_ContextMenu_With_Two_MenuItem (int x, int y)
 		{
-			var cm = new ContextMenu (10, 5,
+			return new ContextMenu (x, y,
 				new MenuBarItem (new MenuItem [] {
 					new MenuItem ("One", "", null),
 					new MenuItem ("Two", "", null)
 				})
 			);
+		}
+
+		[Fact]
+		[AutoInitShutdown]
+		public void Show_Hide_IsShow ()
+		{
+			var cm = Create_ContextMenu_With_Two_MenuItem (10, 5);
 
 			cm.Show ();
 			Assert.True (ContextMenu.IsShow);
@@ -902,5 +907,65 @@ namespace Terminal.Gui.MenuTests {
 			Assert.True (top.Subviews [1].ProcessKey (new KeyEvent (Key.F10 | Key.ShiftMask, new KeyModifiers ())));
 			Assert.Null (tf.ContextMenu.MenuBar);
 		}
+
+		[Fact, AutoInitShutdown]
+		public void RequestStop_While_ContextMenu_Is_Open_Does_Not_Throws ()
+		{
+			var cm = Create_ContextMenu_With_Two_MenuItem (10, 5);
+			var top = Application.Top;
+			var isMenuAllClosed = false;
+			MenuBarItem mi = null;
+			var iterations = -1;
+			Application.Iteration += () => {
+				iterations++;
+				if (iterations == 0) {
+					cm.Show ();
+					Assert.True (ContextMenu.IsShow);
+					mi = cm.MenuBar.Menus [0];
+					mi.Action = () => {
+						var dialog1 = new Dialog ();
+						Application.Run (dialog1);
+
+						Assert.False (ContextMenu.IsShow);
+						Assert.True (isMenuAllClosed);
+					};
+
+					cm.MenuBar.MenuAllClosed += () => isMenuAllClosed = true;
+
+				} else if (iterations == 1) {
+					mi.Action ();
+				} else if (iterations == 2) {
+					Application.RequestStop ();
+				} else if (iterations == 3) {
+					isMenuAllClosed = false;
+					cm.Show ();
+					Assert.True (ContextMenu.IsShow);
+
+					cm.MenuBar.MenuAllClosed += () => isMenuAllClosed = true;
+				} else if (iterations == 4) {
+					var exception = Record.Exception (() => Application.RequestStop ());
+					Assert.Null (exception);
+				} else {
+					Application.RequestStop ();
+				}
+			};
+
+			var isTopClosed = false;
+			top.Closing += (_) => {
+				var dialog2 = new Dialog ();
+				Application.Run (dialog2);
+
+				Assert.False (ContextMenu.IsShow);
+				Assert.True (isMenuAllClosed);
+
+				isTopClosed = true;
+			};
+
+			Application.Run ();
+
+			Assert.True (isTopClosed);
+			Assert.False (ContextMenu.IsShow);
+			Assert.True (isMenuAllClosed);
+		}
 	}
 }

+ 65 - 2
UnitTests/Menus/MenuTests.cs

@@ -254,8 +254,9 @@ Edit
 			// open the menu
 			Assert.True (menu.ProcessHotKey (new KeyEvent (Key.F9, new KeyModifiers ())));
 			Assert.True (menu.IsMenuOpen);
-			Assert.Equal ("_New", miCurrent.Parent.Title);
-			Assert.Equal ("_New doc", miCurrent.Title);
+			// The _New doc isn't enabled because it can't execute and so can't be selected
+			Assert.Equal ("_File", miCurrent.Parent.Title);
+			Assert.Equal ("_New", miCurrent.Title);
 
 			Assert.True (mCurrent.ProcessKey (new KeyEvent (Key.CursorDown, new KeyModifiers ())));
 			Assert.True (menu.IsMenuOpen);
@@ -1746,5 +1747,67 @@ Edit
 			var exception = Record.Exception (() => menu.ProcessColdKey (new KeyEvent (Key.Space, new KeyModifiers ())));
 			Assert.Null (exception);
 		}
+
+		[Fact, AutoInitShutdown]
+		public void CanExecute_ProcessHotKey ()
+		{
+			Window win = null;
+			var menu = new MenuBar (new MenuBarItem [] {
+				new MenuBarItem ("File", new MenuItem [] {
+					new MenuItem ("_New", "", New, CanExecuteNew),
+					new MenuItem ("_Close", "", Close, CanExecuteClose)
+				}),
+			});
+			var top = Application.Top;
+			top.Add (menu);
+
+			bool CanExecuteNew () => win == null;
+
+			void New ()
+			{
+				win = new Window ();
+			}
+
+			bool CanExecuteClose () => win != null;
+
+			void Close ()
+			{
+				win = null;
+			}
+
+			Application.Begin (top);
+
+			Assert.Null (win);
+			Assert.True (CanExecuteNew ());
+			Assert.False (CanExecuteClose ());
+
+			Assert.True (top.ProcessHotKey (new KeyEvent (Key.N | Key.AltMask, new KeyModifiers () { Alt = true })));
+			Application.MainLoop.MainIteration ();
+			Assert.NotNull (win);
+			Assert.False (CanExecuteNew ());
+			Assert.True (CanExecuteClose ());
+		}
+
+		[Fact, AutoInitShutdown]
+		public void Visible_False_Key_Does_Not_Open_And_Close_All_Opened_Menus ()
+		{
+			var menu = new MenuBar (new MenuBarItem [] {
+				new MenuBarItem ("File", new MenuItem [] {
+					new MenuItem ("New", "", null)
+				})
+			});
+			Application.Top.Add (menu);
+			Application.Begin (Application.Top);
+
+			Assert.True (menu.Visible);
+			Assert.True (menu.ProcessHotKey (new KeyEvent (menu.Key, new KeyModifiers ())));
+			Assert.True (menu.IsMenuOpen);
+
+			menu.Visible = false;
+			Assert.False (menu.IsMenuOpen);
+
+			Assert.True (menu.ProcessHotKey (new KeyEvent (menu.Key, new KeyModifiers ())));
+			Assert.False (menu.IsMenuOpen);
+		}
 	}
 }

+ 8 - 0
UnitTests/ReflectionTools.cs

@@ -32,4 +32,12 @@ public static class ReflectionTools {
 		}
 		return null;
 	}
+
+	public static T GetFieldValue<T> (this object obj, string name)
+	{
+		// Set the flags so that private and public fields from instances will be found
+		var bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
+		var field = obj.GetType ().GetField (name, bindingFlags);
+		return (T)field?.GetValue (obj);
+	}
 }

+ 109 - 1
UnitTests/TopLevels/ToplevelTests.cs

@@ -1009,7 +1009,98 @@ namespace Terminal.Gui.TopLevelTests {
 			Application.End (rs);
 
 			Assert.True (isEnter);
-			Assert.False (isLeave);
+			Assert.False (isLeave);  // Leave event cannot be trigger because it v.Enter was performed and v is focused
+			Assert.True (v.HasFocus);
+		}
+
+		[Fact, AutoInitShutdown]
+		public void OnEnter_OnLeave_Triggered_On_Application_Begin_End_With_More_Toplevels ()
+		{
+			var iterations = 0;
+			var steps = new int [5];
+			var isEnterTop = false;
+			var isLeaveTop = false;
+			var vt = new View ();
+			var top = Application.Top;
+			var diag = new Dialog ();
+
+			vt.Enter += (e) => {
+				iterations++;
+				isEnterTop = true;
+				if (iterations == 1) {
+					steps [0] = iterations;
+					Assert.Null (e.View);
+				} else {
+					steps [4] = iterations;
+					Assert.Equal (diag, e.View);
+				}
+			};
+			vt.Leave += (e) => {
+				iterations++;
+				steps [1] = iterations;
+				isLeaveTop = true;
+				Assert.Equal (diag, e.View);
+			};
+			top.Add (vt);
+
+			Assert.False (vt.CanFocus);
+			var exception = Record.Exception (() => top.OnEnter (top));
+			Assert.Null (exception);
+			exception = Record.Exception (() => top.OnLeave (top));
+			Assert.Null (exception);
+
+			vt.CanFocus = true;
+			Application.Begin (top);
+
+			Assert.True (isEnterTop);
+			Assert.False (isLeaveTop);
+
+			isEnterTop = false;
+			var isEnterDiag = false;
+			var isLeaveDiag = false;
+			var vd = new View ();
+			vd.Enter += (e) => {
+				iterations++;
+				steps [2] = iterations;
+				isEnterDiag = true;
+				Assert.Null (e.View);
+			};
+			vd.Leave += (e) => {
+				iterations++;
+				steps [3] = iterations;
+				isLeaveDiag = true;
+				Assert.Equal (top, e.View);
+			};
+			diag.Add (vd);
+
+			Assert.False (vd.CanFocus);
+			exception = Record.Exception (() => diag.OnEnter (diag));
+			Assert.Null (exception);
+			exception = Record.Exception (() => diag.OnLeave (diag));
+			Assert.Null (exception);
+
+			vd.CanFocus = true;
+			var rs = Application.Begin (diag);
+
+			Assert.True (isEnterDiag);
+			Assert.False (isLeaveDiag);
+			Assert.False (isEnterTop);
+			Assert.True (isLeaveTop);
+
+			isEnterDiag = false;
+			isLeaveTop = false;
+			Application.End (rs);
+
+			Assert.False (isEnterDiag);
+			Assert.True (isLeaveDiag);
+			Assert.True (isEnterTop);
+			Assert.False (isLeaveTop);  // Leave event cannot be trigger because it v.Enter was performed and v is focused
+			Assert.True (vt.HasFocus);
+			Assert.Equal (1, steps [0]);
+			Assert.Equal (2, steps [1]);
+			Assert.Equal (3, steps [2]);
+			Assert.Equal (4, steps [3]);
+			Assert.Equal (5, steps [^1]);
 		}
 
 		[Fact, AutoInitShutdown]
@@ -1031,5 +1122,22 @@ namespace Terminal.Gui.TopLevelTests {
 			Application.Driver.GetCursorVisibility (out cursor);
 			Assert.Equal (CursorVisibility.Invisible, cursor);
 		}
+
+		[Fact, AutoInitShutdown]
+		public void Activating_MenuBar_By_Alt_Key_Does_Not_Throw ()
+		{
+			var menu = new MenuBar (new MenuBarItem [] {
+				new MenuBarItem ("Child", new MenuItem [] {
+					new MenuItem ("_Create Child", "", null)
+				})
+			});
+			var topChild = new Toplevel ();
+			topChild.Add (menu);
+			Application.Top.Add (topChild);
+			Application.Begin (Application.Top);
+
+			var exception = Record.Exception (() => topChild.ProcessHotKey (new KeyEvent (Key.AltMask, new KeyModifiers { Alt = true })));
+			Assert.Null (exception);
+		}
 	}
 }

+ 16 - 0
UnitTests/TopLevels/WindowTests.cs

@@ -235,7 +235,23 @@ namespace Terminal.Gui.TopLevelTests {
 │└────────────────┘│
 │ ^Q Quit │ ^O Open│
 └──────────────────┘", output);
+		}
+
+		[Fact, AutoInitShutdown]
+		public void Activating_MenuBar_By_Alt_Key_Does_Not_Throw ()
+		{
+			var menu = new MenuBar (new MenuBarItem [] {
+				new MenuBarItem ("Child", new MenuItem [] {
+					new MenuItem ("_Create Child", "", null)
+				})
+			});
+			var win = new Window ();
+			win.Add (menu);
+			Application.Top.Add (win);
+			Application.Begin (Application.Top);
 
+			var exception = Record.Exception (() => win.ProcessHotKey (new KeyEvent (Key.AltMask, new KeyModifiers { Alt = true })));
+			Assert.Null (exception);
 		}
 	}
 }

+ 10 - 8
UnitTests/UnitTests.csproj

@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <TargetFramework>net7.0</TargetFramework>
     <!-- https://stackoverflow.com/questions/294216/why-does-c-sharp-forbid-generic-attribute-types -->
@@ -7,12 +7,9 @@
     <IsPackable>false</IsPackable>
     <UseDataCollector />
     <!-- Version numbers are automatically updated by gitversion when a release is released -->
-    <!-- In the source tree the version will always be 2.0 for all projects. -->
     <!-- Do not modify these. -->
-    <AssemblyVersion>1.10.1.0</AssemblyVersion>
-    <FileVersion>1.10.1.0</FileVersion>
-    <Version>1.10.1</Version>
-    <InformationalVersion>1.10.1+6.Branch.main.Sha.f7ee66ddbf8dbcfb0d96af7d63789879091670ec</InformationalVersion>
+    <FileVersion>1.0.0.0</FileVersion>
+    <Version>1.0.0</Version>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
     <DefineConstants>TRACE</DefineConstants>
@@ -21,8 +18,8 @@
     <DefineConstants>TRACE;DEBUG_IDISPOSABLE</DefineConstants>
   </PropertyGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.3" />
-    <PackageReference Include="ReportGenerator" Version="5.1.23" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
+    <PackageReference Include="ReportGenerator" Version="5.1.24" />
     <PackageReference Include="System.Collections" Version="4.3.0" />
     <PackageReference Include="xunit" Version="2.5.0" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
@@ -38,6 +35,11 @@
     <ProjectReference Include="..\Terminal.Gui\Terminal.Gui.csproj" />
     <ProjectReference Include="..\UICatalog\UICatalog.csproj" />
   </ItemGroup>
+  <ItemGroup>
+    <None Update="xunit.runner.json">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+  </ItemGroup>
   <PropertyGroup Label="FineCodeCoverage">
     <Enabled>
       True

+ 682 - 43
UnitTests/Views/ScrollBarViewTests.cs

@@ -636,6 +636,81 @@ namespace Terminal.Gui.ViewTests {
 			Assert.True (sbv.OtherScrollBarView.Visible);
 		}
 
+		private class TextViewScrollBarView : ScrollBarView {
+			private TextView _textView;
+
+			public TextViewScrollBarView (TextView textView) : base (textView, true)
+			{
+				_textView = textView;
+
+				ChangedPosition += TextViewScrollBarView_ChangedPosition;
+				OtherScrollBarView.ChangedPosition += OtherScrollBarView_ChangedPosition;
+				VisibleChanged += TextViewScrollBarView_VisibleChanged;
+				OtherScrollBarView.VisibleChanged += OtherScrollBarView_VisibleChanged;
+				_textView.DrawContent += TextView_DrawContent;
+				Initialized += TextViewScrollBarView_Initialized;
+				OtherScrollBarView.Initialized += OtherScrollBarView_Initialized;
+			}
+
+			private void OtherScrollBarView_Initialized (object sender, EventArgs e)
+			{
+				OtherScrollBarView_VisibleChanged ();
+			}
+
+			private void TextViewScrollBarView_Initialized (object sender, EventArgs e)
+			{
+				TextViewScrollBarView_VisibleChanged ();
+			}
+
+			private void TextView_DrawContent (Rect obj)
+			{
+				Size = _textView.Lines;
+				Position = _textView.TopRow;
+				if (OtherScrollBarView != null) {
+					OtherScrollBarView.Size = _textView.Maxlength;
+					OtherScrollBarView.Position = _textView.LeftColumn;
+				}
+				LayoutSubviews ();
+				Refresh ();
+			}
+
+			private void OtherScrollBarView_VisibleChanged ()
+			{
+				if (OtherScrollBarView.Visible && _textView.BottomOffset == 0) {
+					_textView.BottomOffset = 1;
+				} else if (!OtherScrollBarView.Visible && _textView.BottomOffset == 1) {
+					_textView.BottomOffset = 0;
+				}
+			}
+
+			private void TextViewScrollBarView_VisibleChanged ()
+			{
+				if (Visible && _textView.RightOffset == 0) {
+					_textView.RightOffset = 1;
+				} else if (!Visible && _textView.RightOffset == 1) {
+					_textView.RightOffset = 0;
+				}
+			}
+
+			private void OtherScrollBarView_ChangedPosition ()
+			{
+				_textView.LeftColumn = OtherScrollBarView.Position;
+				if (_textView.LeftColumn != OtherScrollBarView.Position) {
+					OtherScrollBarView.Position = _textView.LeftColumn;
+				}
+				_textView.SetNeedsDisplay ();
+			}
+
+			private void TextViewScrollBarView_ChangedPosition ()
+			{
+				_textView.TopRow = Position;
+				if (_textView.TopRow != Position) {
+					Position = _textView.TopRow;
+				}
+				_textView.SetNeedsDisplay ();
+			}
+		}
+
 		[Fact, AutoInitShutdown]
 		public void Hosting_ShowBothScrollIndicator_Invisible ()
 		{
@@ -650,50 +725,8 @@ namespace Terminal.Gui.ViewTests {
 			};
 			win.Add (textView);
 
-			var scrollBar = new ScrollBarView (textView, true);
-
-			scrollBar.ChangedPosition += () => {
-				textView.TopRow = scrollBar.Position;
-				if (textView.TopRow != scrollBar.Position) {
-					scrollBar.Position = textView.TopRow;
-				}
-				textView.SetNeedsDisplay ();
-			};
-
-			scrollBar.OtherScrollBarView.ChangedPosition += () => {
-				textView.LeftColumn = scrollBar.OtherScrollBarView.Position;
-				if (textView.LeftColumn != scrollBar.OtherScrollBarView.Position) {
-					scrollBar.OtherScrollBarView.Position = textView.LeftColumn;
-				}
-				textView.SetNeedsDisplay ();
-			};
-
-			scrollBar.VisibleChanged += () => {
-				if (scrollBar.Visible && textView.RightOffset == 0) {
-					textView.RightOffset = 1;
-				} else if (!scrollBar.Visible && textView.RightOffset == 1) {
-					textView.RightOffset = 0;
-				}
-			};
+			var scrollBar = new TextViewScrollBarView (textView);
 
-			scrollBar.OtherScrollBarView.VisibleChanged += () => {
-				if (scrollBar.OtherScrollBarView.Visible && textView.BottomOffset == 0) {
-					textView.BottomOffset = 1;
-				} else if (!scrollBar.OtherScrollBarView.Visible && textView.BottomOffset == 1) {
-					textView.BottomOffset = 0;
-				}
-			};
-
-			textView.DrawContent += (e) => {
-				scrollBar.Size = textView.Lines;
-				scrollBar.Position = textView.TopRow;
-				if (scrollBar.OtherScrollBarView != null) {
-					scrollBar.OtherScrollBarView.Size = textView.Maxlength;
-					scrollBar.OtherScrollBarView.Position = textView.LeftColumn;
-				}
-				scrollBar.LayoutSubviews ();
-				scrollBar.Refresh ();
-			};
 			Application.Top.Add (win);
 
 			Application.Begin (Application.Top);
@@ -971,5 +1004,611 @@ This is a test
 			Assert.False (sbv.ShowScrollIndicator);
 			Assert.False (sbv.Visible);
 		}
+
+		[Fact, AutoInitShutdown]
+		public void ScrollBarView_Inside_TextView_Always_Scroll ()
+		{
+			var text = "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n1234567890\n";
+			var tv1 = new TextView () {
+				Id = "tv1",
+				Width = Dim.Fill (),
+				Height = Dim.Fill (),
+				Text = text
+			};
+			var f1 = new FrameView () {
+				Width = Dim.Fill (),
+				Height = Dim.Percent (70)
+			};
+			f1.Add (tv1);
+			var sBar1 = new TextViewScrollBarView (tv1);
+			var tv2 = new TextView () {
+				Id = "tv2",
+				Width = Dim.Fill (),
+				Height = Dim.Fill (),
+				Text = text,
+				ReadOnly = true
+			};
+			var f2 = new FrameView () {
+				Y = Pos.Bottom (f1),
+				Width = Dim.Fill (),
+				Height = Dim.Fill ()
+			};
+			f2.Add (tv2);
+			var sBar2 = new TextViewScrollBarView (tv2);
+			var win = new Window () {
+				Width = Dim.Fill (),
+				Height = Dim.Fill ()
+			};
+			win.Add (f1, f2);
+
+			Application.Top.Add (win);
+
+			Application.Begin (Application.Top);
+			((FakeDriver)Application.Driver).SetBufferSize (10, 20);
+
+			// sBar1
+			Assert.True (sBar1.Visible);
+			Assert.Equal (0, sBar1.Position);
+			Assert.Equal (0, tv1.TopRow);
+			Assert.False (sBar1.OtherScrollBarView.Visible);
+			Assert.Equal (0, sBar1.OtherScrollBarView.Position);
+			Assert.Equal (0, tv1.LeftColumn);
+			Assert.Equal (0, tv1.BottomOffset);
+			Assert.Equal (1, tv1.RightOffset);
+			Assert.Equal (new Point (0, 0), tv1.CursorPosition);
+
+			// sBar2
+			Assert.True (sBar2.Visible);
+			Assert.Equal (0, sBar2.Position);
+			Assert.Equal (0, tv2.TopRow);
+			Assert.False (sBar2.OtherScrollBarView.Visible);
+			Assert.Equal (0, sBar2.OtherScrollBarView.Position);
+			Assert.Equal (0, tv2.LeftColumn);
+			Assert.Equal (0, tv2.BottomOffset);
+			Assert.Equal (1, tv2.RightOffset);
+			Assert.Equal (new Point (0, 0), tv2.CursorPosition);
+
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌────────┐
+│┌──────┐│
+││1    ▲││
+││2    ┬││
+││3    │││
+││4    │││
+││5    │││
+││6    │││
+││7    │││
+││8    ┴││
+││9    ░││
+││10   ▼││
+│└──────┘│
+│┌──────┐│
+││1    ▲││
+││2    ┬││
+││3    ┴││
+││4    ▼││
+│└──────┘│
+└────────┘", output);
+
+			Assert.True (sBar1.MouseEvent (new MouseEvent { X = 0, Y = 9, Flags = MouseFlags.Button1Pressed, View = sBar1 }));
+			Assert.True (sBar1.MouseEvent (new MouseEvent { X = 0, Y = 9, Flags = MouseFlags.Button1Released, View = sBar1 }));
+			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 3, Flags = MouseFlags.Button1Pressed, View = sBar2 }));
+			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 3, Flags = MouseFlags.Button1Released, View = sBar2 }));
+			win.Redraw (win.Bounds);
+			Application.MainLoop.MainIteration ();
+			win.Redraw (win.Bounds);
+
+			// sBar1
+			Assert.True (sBar1.Visible);
+			Assert.Equal (2, sBar1.Position);
+			Assert.Equal (2, tv1.TopRow);
+			Assert.True (sBar1.OtherScrollBarView.Visible);
+			Assert.Equal (0, sBar1.OtherScrollBarView.Position);
+			Assert.Equal (0, tv1.LeftColumn);
+			Assert.Equal (1, tv1.BottomOffset);
+			Assert.Equal (1, tv1.RightOffset);
+			Assert.Equal (new Point (0, 0), tv1.CursorPosition);
+
+			// sBar2
+			Assert.True (sBar2.Visible);
+			Assert.Equal (1, sBar2.Position);
+			Assert.Equal (1, tv2.TopRow);
+			Assert.False (sBar2.OtherScrollBarView.Visible);
+			Assert.Equal (0, sBar2.OtherScrollBarView.Position);
+			Assert.Equal (0, tv2.LeftColumn);
+			Assert.Equal (0, tv2.BottomOffset);
+			Assert.Equal (1, tv2.RightOffset);
+			Assert.Equal (new Point (0, 0), tv2.CursorPosition);
+
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌────────┐
+│┌──────┐│
+││3    ▲││
+││4    ░││
+││5    ┬││
+││6    │││
+││7    │││
+││8    │││
+││9    │││
+││10   ┴││
+││12345▼││
+││◄├┤░► ││
+│└──────┘│
+│┌──────┐│
+││2    ▲││
+││3    ┬││
+││4    ┴││
+││5    ▼││
+│└──────┘│
+└────────┘", output);
+
+			Assert.True (sBar1.MouseEvent (new MouseEvent { X = 0, Y = 8, Flags = MouseFlags.Button1Pressed, View = sBar1 }));
+			Assert.True (sBar1.MouseEvent (new MouseEvent { X = 0, Y = 8, Flags = MouseFlags.Button1Released, View = sBar1 }));
+			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 3, Flags = MouseFlags.Button1Pressed, View = sBar2 }));
+			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 3, Flags = MouseFlags.Button1Released, View = sBar2 }));
+			win.Redraw (win.Bounds);
+			Application.MainLoop.MainIteration ();
+			win.Redraw (win.Bounds);
+
+			// sBar1
+			Assert.True (sBar1.Visible);
+			Assert.Equal (3, sBar1.Position);
+			Assert.Equal (3, tv1.TopRow);
+			Assert.True (sBar1.OtherScrollBarView.Visible);
+			Assert.Equal (0, sBar1.OtherScrollBarView.Position);
+			Assert.Equal (0, tv1.LeftColumn);
+			Assert.Equal (1, tv1.BottomOffset);
+			Assert.Equal (1, tv1.RightOffset);
+			Assert.Equal (new Point (0, 0), tv1.CursorPosition);
+
+			// sBar2
+			Assert.True (sBar2.Visible);
+			Assert.Equal (2, sBar2.Position);
+			Assert.Equal (2, tv2.TopRow);
+			Assert.False (sBar2.OtherScrollBarView.Visible);
+			Assert.Equal (0, sBar2.OtherScrollBarView.Position);
+			Assert.Equal (0, tv2.LeftColumn);
+			Assert.Equal (0, tv2.BottomOffset);
+			Assert.Equal (1, tv2.RightOffset);
+			Assert.Equal (new Point (0, 0), tv2.CursorPosition);
+
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌────────┐
+│┌──────┐│
+││4    ▲││
+││5    ░││
+││6    ┬││
+││7    │││
+││8    │││
+││9    │││
+││10   │││
+││12345┴││
+││     ▼││
+││◄├┤░► ││
+│└──────┘│
+│┌──────┐│
+││3    ▲││
+││4    ┬││
+││5    ┴││
+││6    ▼││
+│└──────┘│
+└────────┘", output);
+
+			Assert.True (sBar1.OtherScrollBarView.MouseEvent (new MouseEvent { X = 4, Y = 0, Flags = MouseFlags.Button1Pressed, View = sBar1 }));
+			Assert.True (sBar1.OtherScrollBarView.MouseEvent (new MouseEvent { X = 4, Y = 0, Flags = MouseFlags.Button1Released, View = sBar1 }));
+			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 3, Flags = MouseFlags.Button1Pressed, View = sBar2 }));
+			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 3, Flags = MouseFlags.Button1Released, View = sBar2 }));
+			win.Redraw (win.Bounds);
+			Application.MainLoop.MainIteration ();
+			win.Redraw (win.Bounds);
+
+			// sBar1
+			Assert.True (sBar1.Visible);
+			Assert.Equal (3, sBar1.Position);
+			Assert.Equal (3, tv1.TopRow);
+			Assert.True (sBar1.OtherScrollBarView.Visible);
+			Assert.Equal (1, sBar1.OtherScrollBarView.Position);
+			Assert.Equal (1, tv1.LeftColumn);
+			Assert.Equal (1, tv1.BottomOffset);
+			Assert.Equal (1, tv1.RightOffset);
+			Assert.Equal (new Point (0, 0), tv1.CursorPosition);
+
+			// sBar2
+			Assert.True (sBar2.Visible);
+			Assert.Equal (3, sBar2.Position);
+			Assert.Equal (3, tv2.TopRow);
+			Assert.False (sBar2.OtherScrollBarView.Visible);
+			Assert.Equal (0, sBar2.OtherScrollBarView.Position);
+			Assert.Equal (0, tv2.LeftColumn);
+			Assert.Equal (0, tv2.BottomOffset);
+			Assert.Equal (1, tv2.RightOffset);
+			Assert.Equal (new Point (0, 0), tv2.CursorPosition);
+
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌────────┐
+│┌──────┐│
+││     ▲││
+││     ░││
+││     ┬││
+││     │││
+││     │││
+││     │││
+││0    │││
+││23456┴││
+││     ▼││
+││◄├─┤► ││
+│└──────┘│
+│┌──────┐│
+││4    ▲││
+││5    ┬││
+││6    ┴││
+││7    ▼││
+│└──────┘│
+└────────┘", output);
+
+			Assert.True (sBar1.OtherScrollBarView.MouseEvent (new MouseEvent { X = 4, Y = 0, Flags = MouseFlags.Button1Pressed, View = sBar1 }));
+			Assert.True (sBar1.OtherScrollBarView.MouseEvent (new MouseEvent { X = 4, Y = 0, Flags = MouseFlags.Button1Released, View = sBar1 }));
+			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 3, Flags = MouseFlags.Button1Pressed, View = sBar2 }));
+			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 3, Flags = MouseFlags.Button1Released, View = sBar2 }));
+			win.Redraw (win.Bounds);
+			Application.MainLoop.MainIteration ();
+			win.Redraw (win.Bounds);
+
+			// sBar1
+			Assert.True (sBar1.Visible);
+			Assert.Equal (3, sBar1.Position);
+			Assert.Equal (3, tv1.TopRow);
+			Assert.True (sBar1.OtherScrollBarView.Visible);
+			Assert.Equal (2, sBar1.OtherScrollBarView.Position);
+			Assert.Equal (2, tv1.LeftColumn);
+			Assert.Equal (1, tv1.BottomOffset);
+			Assert.Equal (1, tv1.RightOffset);
+			Assert.Equal (new Point (0, 0), tv1.CursorPosition);
+
+			// sBar2
+			Assert.True (sBar2.Visible);
+			Assert.Equal (4, sBar2.Position);
+			Assert.Equal (4, tv2.TopRow);
+			Assert.False (sBar2.OtherScrollBarView.Visible);
+			Assert.Equal (0, sBar2.OtherScrollBarView.Position);
+			Assert.Equal (0, tv2.LeftColumn);
+			Assert.Equal (0, tv2.BottomOffset);
+			Assert.Equal (1, tv2.RightOffset);
+			Assert.Equal (new Point (0, 0), tv2.CursorPosition);
+
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌────────┐
+│┌──────┐│
+││     ▲││
+││     ░││
+││     ┬││
+││     │││
+││     │││
+││     │││
+││     │││
+││34567┴││
+││     ▼││
+││◄├─┤► ││
+│└──────┘│
+│┌──────┐│
+││5    ▲││
+││6    ┬││
+││7    ┴││
+││8    ▼││
+│└──────┘│
+└────────┘", output);
+
+			Assert.True (sBar1.OtherScrollBarView.MouseEvent (new MouseEvent { X = 4, Y = 0, Flags = MouseFlags.Button1Pressed, View = sBar1 }));
+			Assert.True (sBar1.OtherScrollBarView.MouseEvent (new MouseEvent { X = 4, Y = 0, Flags = MouseFlags.Button1Released, View = sBar1 }));
+			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 3, Flags = MouseFlags.Button1Pressed, View = sBar2 }));
+			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 3, Flags = MouseFlags.Button1Released, View = sBar2 }));
+			win.Redraw (win.Bounds);
+			Application.MainLoop.MainIteration ();
+			win.Redraw (win.Bounds);
+
+			// sBar1
+			Assert.True (sBar1.Visible);
+			Assert.Equal (3, sBar1.Position);
+			Assert.Equal (3, tv1.TopRow);
+			Assert.True (sBar1.OtherScrollBarView.Visible);
+			Assert.Equal (3, sBar1.OtherScrollBarView.Position);
+			Assert.Equal (3, tv1.LeftColumn);
+			Assert.Equal (1, tv1.BottomOffset);
+			Assert.Equal (1, tv1.RightOffset);
+			Assert.Equal (new Point (0, 0), tv1.CursorPosition);
+
+			// sBar2
+			Assert.True (sBar2.Visible);
+			Assert.Equal (5, sBar2.Position);
+			Assert.Equal (5, tv2.TopRow);
+			Assert.False (sBar2.OtherScrollBarView.Visible);
+			Assert.Equal (0, sBar2.OtherScrollBarView.Position);
+			Assert.Equal (0, tv2.LeftColumn);
+			Assert.Equal (0, tv2.BottomOffset);
+			Assert.Equal (1, tv2.RightOffset);
+			Assert.Equal (new Point (0, 0), tv2.CursorPosition);
+
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌────────┐
+│┌──────┐│
+││     ▲││
+││     ░││
+││     ┬││
+││     │││
+││     │││
+││     │││
+││     │││
+││45678┴││
+││     ▼││
+││◄├─┤► ││
+│└──────┘│
+│┌──────┐│
+││6    ▲││
+││7    ┬││
+││8    ┴││
+││9    ▼││
+│└──────┘│
+└────────┘", output);
+
+			Assert.True (sBar1.OtherScrollBarView.MouseEvent (new MouseEvent { X = 4, Y = 0, Flags = MouseFlags.Button1Pressed, View = sBar1 }));
+			Assert.True (sBar1.OtherScrollBarView.MouseEvent (new MouseEvent { X = 4, Y = 0, Flags = MouseFlags.Button1Released, View = sBar1 }));
+			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 3, Flags = MouseFlags.Button1Pressed, View = sBar2 }));
+			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 3, Flags = MouseFlags.Button1Released, View = sBar2 }));
+			win.Redraw (win.Bounds);
+			Application.MainLoop.MainIteration ();
+			win.Redraw (win.Bounds);
+
+			// sBar1
+			Assert.True (sBar1.Visible);
+			Assert.Equal (3, sBar1.Position);
+			Assert.Equal (3, tv1.TopRow);
+			Assert.True (sBar1.OtherScrollBarView.Visible);
+			Assert.Equal (4, sBar1.OtherScrollBarView.Position);
+			Assert.Equal (4, tv1.LeftColumn);
+			Assert.Equal (1, tv1.BottomOffset);
+			Assert.Equal (1, tv1.RightOffset);
+			Assert.Equal (new Point (0, 0), tv1.CursorPosition);
+
+			// sBar2
+			Assert.True (sBar2.Visible);
+			Assert.Equal (6, sBar2.Position);
+			Assert.Equal (6, tv2.TopRow);
+			Assert.False (sBar2.OtherScrollBarView.Visible);
+			Assert.Equal (0, sBar2.OtherScrollBarView.Position);
+			Assert.Equal (0, tv2.LeftColumn);
+			Assert.Equal (0, tv2.BottomOffset);
+			Assert.Equal (1, tv2.RightOffset);
+			Assert.Equal (new Point (0, 0), tv2.CursorPosition);
+
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌────────┐
+│┌──────┐│
+││     ▲││
+││     ░││
+││     ┬││
+││     │││
+││     │││
+││     │││
+││     │││
+││56789┴││
+││     ▼││
+││◄░├┤► ││
+│└──────┘│
+│┌──────┐│
+││7    ▲││
+││8    ┬││
+││9    ┴││
+││10   ▼││
+│└──────┘│
+└────────┘", output);
+
+			Assert.True (sBar1.OtherScrollBarView.MouseEvent (new MouseEvent { X = 4, Y = 0, Flags = MouseFlags.Button1Pressed, View = sBar1 }));
+			Assert.True (sBar1.OtherScrollBarView.MouseEvent (new MouseEvent { X = 4, Y = 0, Flags = MouseFlags.Button1Released, View = sBar1 }));
+			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 3, Flags = MouseFlags.Button1Pressed, View = sBar2 }));
+			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 3, Flags = MouseFlags.Button1Released, View = sBar2 }));
+			win.Redraw (win.Bounds);
+			Application.MainLoop.MainIteration ();
+			win.Redraw (win.Bounds);
+
+			// sBar1
+			Assert.True (sBar1.Visible);
+			Assert.Equal (3, sBar1.Position);
+			Assert.Equal (3, tv1.TopRow);
+			Assert.True (sBar1.OtherScrollBarView.Visible);
+			Assert.Equal (5, sBar1.OtherScrollBarView.Position);
+			Assert.Equal (5, tv1.LeftColumn);
+			Assert.Equal (1, tv1.BottomOffset);
+			Assert.Equal (1, tv1.RightOffset);
+			Assert.Equal (new Point (0, 0), tv1.CursorPosition);
+
+			// sBar2
+			Assert.True (sBar2.Visible);
+			Assert.Equal (8, sBar2.Position);
+			Assert.Equal (8, tv2.TopRow);
+			Assert.True (sBar2.OtherScrollBarView.Visible);
+			Assert.Equal (0, sBar2.OtherScrollBarView.Position);
+			Assert.Equal (0, tv2.LeftColumn);
+			Assert.Equal (1, tv2.BottomOffset);
+			Assert.Equal (1, tv2.RightOffset);
+			Assert.Equal (new Point (0, 0), tv2.CursorPosition);
+
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌────────┐
+│┌──────┐│
+││     ▲││
+││     ░││
+││     ┬││
+││     │││
+││     │││
+││     │││
+││     │││
+││67890┴││
+││     ▼││
+││◄░├┤► ││
+│└──────┘│
+│┌──────┐│
+││9    ▲││
+││10   ◊││
+││12345▼││
+││◄├┤░► ││
+│└──────┘│
+└────────┘", output);
+
+			Assert.True (sBar1.MouseEvent (new MouseEvent { X = 0, Y = 0, Flags = MouseFlags.Button1Pressed, View = sBar1 }));
+			Assert.True (sBar1.MouseEvent (new MouseEvent { X = 0, Y = 0, Flags = MouseFlags.Button1Released, View = sBar1 }));
+			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 2, Flags = MouseFlags.Button1Pressed, View = sBar2 }));
+			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 2, Flags = MouseFlags.Button1Released, View = sBar2 }));
+			win.Redraw (win.Bounds);
+			Application.MainLoop.MainIteration ();
+			win.Redraw (win.Bounds);
+
+			// sBar1
+			Assert.True (sBar1.Visible);
+			Assert.Equal (2, sBar1.Position);
+			Assert.Equal (2, tv1.TopRow);
+			Assert.True (sBar1.OtherScrollBarView.Visible);
+			Assert.Equal (5, sBar1.OtherScrollBarView.Position);
+			Assert.Equal (5, tv1.LeftColumn);
+			Assert.Equal (1, tv1.BottomOffset);
+			Assert.Equal (1, tv1.RightOffset);
+			Assert.Equal (new Point (0, 0), tv1.CursorPosition);
+
+			// sBar2
+			Assert.True (sBar2.Visible);
+			Assert.Equal (9, sBar2.Position);
+			Assert.Equal (9, tv2.TopRow);
+			Assert.True (sBar2.OtherScrollBarView.Visible);
+			Assert.Equal (0, sBar2.OtherScrollBarView.Position);
+			Assert.Equal (0, tv2.LeftColumn);
+			Assert.Equal (1, tv2.BottomOffset);
+			Assert.Equal (1, tv2.RightOffset);
+			Assert.Equal (new Point (0, 0), tv2.CursorPosition);
+
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌────────┐
+│┌──────┐│
+││     ▲││
+││     ░││
+││     ┬││
+││     │││
+││     │││
+││     │││
+││     │││
+││     ┴││
+││67890▼││
+││◄░├┤► ││
+│└──────┘│
+│┌──────┐│
+││10   ▲││
+││12345◊││
+││     ▼││
+││◄├┤░► ││
+│└──────┘│
+└────────┘", output);
+
+			Assert.True (sBar1.MouseEvent (new MouseEvent { X = 0, Y = 0, Flags = MouseFlags.Button1Pressed, View = sBar1 }));
+			Assert.True (sBar1.MouseEvent (new MouseEvent { X = 0, Y = 0, Flags = MouseFlags.Button1Released, View = sBar1 }));
+			Assert.True (sBar2.OtherScrollBarView.MouseEvent (new MouseEvent { X = 4, Y = 0, Flags = MouseFlags.Button1Pressed, View = sBar2 }));
+			Assert.True (sBar2.OtherScrollBarView.MouseEvent (new MouseEvent { X = 4, Y = 0, Flags = MouseFlags.Button1Released, View = sBar2 }));
+			win.Redraw (win.Bounds);
+			Application.MainLoop.MainIteration ();
+			win.Redraw (win.Bounds);
+
+			// sBar1
+			Assert.True (sBar1.Visible);
+			Assert.Equal (1, sBar1.Position);
+			Assert.Equal (1, tv1.TopRow);
+			Assert.True (sBar1.OtherScrollBarView.Visible);
+			Assert.Equal (5, sBar1.OtherScrollBarView.Position);
+			Assert.Equal (5, tv1.LeftColumn);
+			Assert.Equal (1, tv1.BottomOffset);
+			Assert.Equal (1, tv1.RightOffset);
+			Assert.Equal (new Point (0, 0), tv1.CursorPosition);
+
+			// sBar2
+			Assert.True (sBar2.Visible);
+			Assert.Equal (9, sBar2.Position);
+			Assert.Equal (9, tv2.TopRow);
+			Assert.True (sBar2.OtherScrollBarView.Visible);
+			Assert.Equal (1, sBar2.OtherScrollBarView.Position);
+			Assert.Equal (1, tv2.LeftColumn);
+			Assert.Equal (1, tv2.BottomOffset);
+			Assert.Equal (1, tv2.RightOffset);
+			Assert.Equal (new Point (0, 0), tv2.CursorPosition);
+
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌────────┐
+│┌──────┐│
+││     ▲││
+││     ┬││
+││     │││
+││     │││
+││     │││
+││     │││
+││     ┴││
+││     ░││
+││     ▼││
+││◄░├┤► ││
+│└──────┘│
+│┌──────┐│
+││0    ▲││
+││23456◊││
+││     ▼││
+││◄├─┤► ││
+│└──────┘│
+└────────┘", output);
+
+			Assert.True (sBar1.MouseEvent (new MouseEvent { X = 0, Y = 0, Flags = MouseFlags.Button1Pressed, View = sBar1 }));
+			Assert.True (sBar1.MouseEvent (new MouseEvent { X = 0, Y = 0, Flags = MouseFlags.Button1Released, View = sBar1 }));
+			Assert.True (sBar2.OtherScrollBarView.MouseEvent (new MouseEvent { X = 4, Y = 0, Flags = MouseFlags.Button1Pressed, View = sBar2 }));
+			Assert.True (sBar2.OtherScrollBarView.MouseEvent (new MouseEvent { X = 4, Y = 0, Flags = MouseFlags.Button1Released, View = sBar2 }));
+			win.Redraw (win.Bounds);
+			Application.MainLoop.MainIteration ();
+			win.Redraw (win.Bounds);
+
+			// sBar1
+			Assert.True (sBar1.Visible);
+			Assert.Equal (0, sBar1.Position);
+			Assert.Equal (0, tv1.TopRow);
+			Assert.False (sBar1.OtherScrollBarView.Visible);
+			Assert.Equal (0, sBar1.OtherScrollBarView.Position);
+			Assert.Equal (0, tv1.LeftColumn);
+			Assert.Equal (0, tv1.BottomOffset);
+			Assert.Equal (1, tv1.RightOffset);
+			Assert.Equal (new Point (0, 0), tv1.CursorPosition);
+
+			// sBar2
+			Assert.True (sBar2.Visible);
+			Assert.Equal (9, sBar2.Position);
+			Assert.Equal (9, tv2.TopRow);
+			Assert.True (sBar2.OtherScrollBarView.Visible);
+			Assert.Equal (2, sBar2.OtherScrollBarView.Position);
+			Assert.Equal (2, tv2.LeftColumn);
+			Assert.Equal (1, tv2.BottomOffset);
+			Assert.Equal (1, tv2.RightOffset);
+			Assert.Equal (new Point (0, 0), tv2.CursorPosition);
+
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌────────┐
+│┌──────┐│
+││1    ▲││
+││2    ┬││
+││3    │││
+││4    │││
+││5    │││
+││6    │││
+││7    │││
+││8    ┴││
+││9    ░││
+││10   ▼││
+│└──────┘│
+│┌──────┐│
+││     ▲││
+││34567◊││
+││     ▼││
+││◄├─┤► ││
+│└──────┘│
+└────────┘", output);
+		}
 	}
 }

+ 40 - 1
UnitTests/Views/StatusBarTests.cs

@@ -18,12 +18,13 @@ namespace Terminal.Gui.ViewTests {
 			Assert.Equal (Key.CtrlMask | Key.Q, si.Shortcut);
 			Assert.Equal ("~^Q~ Quit", si.Title);
 			Assert.Null (si.Action);
+			Assert.True (si.IsEnabled ());
 			si = new StatusItem (Key.CtrlMask | Key.Q, "~^Q~ Quit", () => { });
 			Assert.NotNull (si.Action);
 		}
 
 		[Fact]
-		public void StatusBar_Contructor_Default ()
+		public void StatusBar_Constructor_Default ()
 		{
 			var sb = new StatusBar ();
 
@@ -155,5 +156,43 @@ CTRL-O Open {Application.Driver.VLine} CTRL-Q Quit
 			Assert.Equal ("~^A~ Save As", sb.Items [1].Title);
 			Assert.Equal ("~^Q~ Quit", sb.Items [^1].Title);
 		}
+
+		[Fact, AutoInitShutdown]
+		public void CanExecute_ProcessHotKey ()
+		{
+			Window win = null;
+			var statusBar = new StatusBar (new StatusItem [] {
+				new StatusItem (Key.CtrlMask | Key.N, "~^N~ New", New, CanExecuteNew),
+				new StatusItem (Key.CtrlMask | Key.C, "~^C~ Close", Close, CanExecuteClose)
+			});
+			var top = Application.Top;
+			top.Add (statusBar);
+
+			bool CanExecuteNew () => win == null;
+
+			void New ()
+			{
+				win = new Window ();
+			}
+
+			bool CanExecuteClose () => win != null;
+
+			void Close ()
+			{
+				win = null;
+			}
+
+			Application.Begin (top);
+
+			Assert.Null (win);
+			Assert.True (CanExecuteNew ());
+			Assert.False (CanExecuteClose ());
+
+			Assert.True (top.ProcessHotKey (new KeyEvent (Key.N | Key.CtrlMask, new KeyModifiers () { Alt = true })));
+			Application.MainLoop.MainIteration ();
+			Assert.NotNull (win);
+			Assert.False (CanExecuteNew ());
+			Assert.True (CanExecuteClose ());
+		}
 	}
 }

+ 32 - 2
UnitTests/Views/TextViewTests.cs

@@ -6634,11 +6634,11 @@ This is the second line.
 			Kill_Delete_WordBackward ();
 			Assert.Equal (expectedEventCount, eventcount);
 
-			expectedEventCount += 1;
+			expectedEventCount += 2;
 			Kill_To_End_Delete_Forwards_And_Copy_To_The_Clipboard ();
 			Assert.Equal (expectedEventCount, eventcount);
 
-			expectedEventCount += 1;
+			expectedEventCount += 2;
 			Kill_To_Start_Delete_Backwards_And_Copy_To_The_Clipboard ();
 			Assert.Equal (expectedEventCount, eventcount);
 		}
@@ -6827,5 +6827,35 @@ This is the second line.
 			Assert.Null (exception);
 			Assert.Equal (textToReplace, tv.Text);
 		}
+
+		[Fact]
+		[TextViewTestsAutoInitShutdown]
+		public void Paste_Always_Render_Screen ()
+		{
+			var win = new Window ();
+			win.Border.BorderStyle = BorderStyle.None;
+			win.Border.DrawMarginFrame = false;
+			win.Add (_textView);
+			Application.Top.Add (win);
+			Application.Begin (Application.Top);
+
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+TAB to jump between text field", output);
+
+			Assert.True (_textView.ProcessKey (new KeyEvent (Key.End | Key.ShiftMask, new KeyModifiers () { Shift = true }))); // Select line
+			Assert.Equal ("TAB to jump between text fields.", _textView.SelectedText);
+			Assert.True (_textView.ProcessKey (new KeyEvent (Key.C | Key.CtrlMask, new KeyModifiers ()))); // Copy
+			Assert.Equal ("TAB to jump between text fields.", Clipboard.Contents);
+			Assert.True (_textView.ProcessKey (new KeyEvent (Key.End, new KeyModifiers ()))); // Go to end of line
+			Assert.True (_textView.ProcessKey (new KeyEvent (Key.Enter, new KeyModifiers ()))); // New line
+			Assert.Equal (new Point (0, 1), _textView.CursorPosition);
+			Assert.Equal ("", _textView.SelectedText);
+			Assert.True (_textView.ProcessKey (new KeyEvent (Key.Y | Key.CtrlMask, new KeyModifiers ()))); // Paste
+			Assert.Equal ($"TAB to jump between text fields.{Environment.NewLine}TAB to jump between text fields.", _textView.Text);
+			win.Redraw (win.Bounds);
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+ to jump between text fields.
+ to jump between text fields.", output);
+		}
 	}
 }

+ 43 - 0
UnitTests/Views/ViewTests.cs

@@ -1915,11 +1915,25 @@ Y
 			Assert.True (view1.CanFocus);
 			Assert.True (view1.HasFocus);
 			Assert.True (view2.CanFocus);
+			Assert.False (view2.HasFocus); // Only one of the most focused toplevels view can have focus
+
+			Assert.True (Application.Top.ProcessKey (new KeyEvent (Key.Tab, new KeyModifiers ())));
+			Assert.True (view1.CanFocus);
+			Assert.False (view1.HasFocus); // Only one of the most focused toplevels view can have focus
+			Assert.True (view2.CanFocus);
 			Assert.True (view2.HasFocus);
 
+			Assert.True (Application.Top.ProcessKey (new KeyEvent (Key.Tab, new KeyModifiers ())));
+			Assert.True (view1.CanFocus);
+			Assert.True (view1.HasFocus);
+			Assert.True (view2.CanFocus);
+			Assert.False (view2.HasFocus); // Only one of the most focused toplevels view can have focus
+
 			view1.CanFocus = false;
 			Assert.False (view1.CanFocus);
 			Assert.False (view1.HasFocus);
+			Assert.True (view2.CanFocus);
+			Assert.True (view2.HasFocus);
 			Assert.Equal (win2, Application.Current.Focused);
 			Assert.Equal (view2, Application.Current.MostFocused);
 		}
@@ -1941,11 +1955,26 @@ Y
 			Assert.True (view1.CanFocus);
 			Assert.True (view1.HasFocus);
 			Assert.True (view2.CanFocus);
+			Assert.False (view2.HasFocus); // Only one of the most focused toplevels view can have focus
+
+			Assert.True (Application.Top.ProcessKey (new KeyEvent (Key.Tab | Key.CtrlMask, new KeyModifiers ())));
+			Assert.True (Application.Top.ProcessKey (new KeyEvent (Key.Tab | Key.CtrlMask, new KeyModifiers ())));
+			Assert.True (view1.CanFocus);
+			Assert.False (view1.HasFocus); // Only one of the most focused toplevels view can have focus
+			Assert.True (view2.CanFocus);
 			Assert.True (view2.HasFocus);
 
+			Assert.True (Application.Top.ProcessKey (new KeyEvent (Key.Tab | Key.CtrlMask, new KeyModifiers ())));
+			Assert.True (view1.CanFocus);
+			Assert.True (view1.HasFocus);
+			Assert.True (view2.CanFocus);
+			Assert.False (view2.HasFocus); // Only one of the most focused toplevels view can have focus
+
 			view1.CanFocus = false;
 			Assert.False (view1.CanFocus);
 			Assert.False (view1.HasFocus);
+			Assert.True (view2.CanFocus);
+			Assert.False (view2.HasFocus);
 			Assert.Equal (win1, Application.Current.Focused);
 			Assert.Equal (view12, Application.Current.MostFocused);
 		}
@@ -1966,13 +1995,27 @@ Y
 			Assert.True (view1.CanFocus);
 			Assert.True (view1.HasFocus);
 			Assert.True (view2.CanFocus);
+			Assert.False (view2.HasFocus); // Only one of the most focused toplevels view can have focus
+
+			Assert.True (Application.Top.ProcessKey (new KeyEvent (Key.Tab | Key.CtrlMask, new KeyModifiers ())));
+			Assert.True (view1.CanFocus);
+			Assert.False (view1.HasFocus); // Only one of the most focused toplevels view can have focus
+			Assert.True (view2.CanFocus);
 			Assert.True (view2.HasFocus);
 
+			Assert.True (Application.Top.ProcessKey (new KeyEvent (Key.Tab | Key.CtrlMask, new KeyModifiers ())));
+			Assert.True (view1.CanFocus);
+			Assert.True (view1.HasFocus);
+			Assert.True (view2.CanFocus);
+			Assert.False (view2.HasFocus); // Only one of the most focused toplevels view can have focus
+
 			win1.CanFocus = false;
 			Assert.False (view1.CanFocus);
 			Assert.False (view1.HasFocus);
 			Assert.False (win1.CanFocus);
 			Assert.False (win1.HasFocus);
+			Assert.True (view2.CanFocus);
+			Assert.True (view2.HasFocus);
 			Assert.Equal (win2, Application.Current.Focused);
 			Assert.Equal (view2, Application.Current.MostFocused);
 		}

+ 2 - 1
UnitTests/xunit.runner.json

@@ -1,5 +1,6 @@
 {
   "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json",
   "parallelizeTestCollections": false,
-  "parallelizeAssembly": false
+  "parallelizeAssembly": false,
+  "stopOnFail": true
 }

+ 2 - 2
global.json

@@ -1,6 +1,6 @@
 {
   "sdk":{
-    "version":"7.0.100",
-    "rollForward":"latestMajor"
+    "version":"7.0.200",
+    "rollForward":"latestMinor"
   }
 }

+ 12 - 0
nuget.config

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+  <config>
+    <add key="defaultPushSource" value="https://api.nuget.org/v3/index.json" />
+  </config>
+  <packageSources>
+
+    <!--To inherit the global NuGet package sources remove the <clear/> line below -->
+    <clear />
+    <add key="nuget" value="https://api.nuget.org/v3/index.json" />
+  </packageSources>
+</configuration>