mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-10 08:11:06 +00:00
Compare commits
155 Commits
dev/pabhoj
...
dev/migrie
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e301b010d | ||
|
|
27887603b4 | ||
|
|
87c840b381 | ||
|
|
91616885db | ||
|
|
089c015347 | ||
|
|
dea94d51ad | ||
|
|
83649075a7 | ||
|
|
e9375d67f1 | ||
|
|
4b4e057a75 | ||
|
|
35c82d37f0 | ||
|
|
4acbffb0e3 | ||
|
|
aaefdf4408 | ||
|
|
1f2bb760eb | ||
|
|
5cb4ab1a9e | ||
|
|
cfe879da96 | ||
|
|
ac62de78a2 | ||
|
|
9870eb1d39 | ||
|
|
e06ce39811 | ||
|
|
997212f9c5 | ||
|
|
2ab0a50a11 | ||
|
|
376939cef8 | ||
|
|
15a7d49f4d | ||
|
|
ef595b497a | ||
|
|
97676cedd8 | ||
|
|
91bf709ca6 | ||
|
|
7abed9f6fb | ||
|
|
b4a52c847f | ||
|
|
3f21996589 | ||
|
|
fd74d59c6c | ||
|
|
04478d1df0 | ||
|
|
589286a357 | ||
|
|
d74b66a0a7 | ||
|
|
ae5b04f7b2 | ||
|
|
3434208947 | ||
|
|
32379c29f0 | ||
|
|
cd2166aedf | ||
|
|
66f4f9d9ea | ||
|
|
bbc570d107 | ||
|
|
238b5c2e22 | ||
|
|
55dd4af9cb | ||
|
|
2970017243 | ||
|
|
1faf67fba3 | ||
|
|
024b9fc0f4 | ||
|
|
aa4e9f5414 | ||
|
|
76b00e3b31 | ||
|
|
07d58a800c | ||
|
|
f893f7516a | ||
|
|
702e15ce5a | ||
|
|
ff23726b7c | ||
|
|
7675354b2d | ||
|
|
16028dee8b | ||
|
|
c6b67aad4b | ||
|
|
bd403dc8e7 | ||
|
|
f025c53dba | ||
|
|
66ecb0bd63 | ||
|
|
95a19624a4 | ||
|
|
86dfefa690 | ||
|
|
478c2c3613 | ||
|
|
a579566a1d | ||
|
|
f1b6b17c58 | ||
|
|
7b8a53c030 | ||
|
|
d6a9e9ffb6 | ||
|
|
eb1d6b599e | ||
|
|
2cd2351deb | ||
|
|
843e61aa8a | ||
|
|
f785168aac | ||
|
|
8fba292bd7 | ||
|
|
9e8427d81e | ||
|
|
059b53bf94 | ||
|
|
4d48f305a1 | ||
|
|
06e2317012 | ||
|
|
4071df73ba | ||
|
|
19605bf75b | ||
|
|
8962f88f90 | ||
|
|
eb4b2a7534 | ||
|
|
72a4e936cc | ||
|
|
1f083cbd89 | ||
|
|
1ac7fe16ae | ||
|
|
ff7a632f0f | ||
|
|
797ebae6e1 | ||
|
|
4f7e99123a | ||
|
|
b8cd2b239f | ||
|
|
24a53d4968 | ||
|
|
023eb75550 | ||
|
|
0b97c7b5ca | ||
|
|
f7d93849a8 | ||
|
|
7707716b08 | ||
|
|
6436e712e7 | ||
|
|
08c2f350e6 | ||
|
|
a5fb91dd4c | ||
|
|
848314ef17 | ||
|
|
deffbbc7f5 | ||
|
|
f12ee745ef | ||
|
|
9eb191d545 | ||
|
|
bfd910c4ca | ||
|
|
715844b01c | ||
|
|
dfd345405a | ||
|
|
daa2ef139f | ||
|
|
44ac527f31 | ||
|
|
52f7664d01 | ||
|
|
223e270778 | ||
|
|
eac5cebbc6 | ||
|
|
7d4ee45e4e | ||
|
|
02fa24cb12 | ||
|
|
f898855c82 | ||
|
|
a38fe5f1a6 | ||
|
|
bbe32f80e5 | ||
|
|
446d07e79f | ||
|
|
a74b45aa2c | ||
|
|
c588a9c75d | ||
|
|
082c63bde5 | ||
|
|
91977bec5a | ||
|
|
07a1a07e47 | ||
|
|
ce8f8fb618 | ||
|
|
538285cc45 | ||
|
|
b1ee82ae18 | ||
|
|
358edd520a | ||
|
|
bf0f516634 | ||
|
|
99308b43e2 | ||
|
|
2a9eefc2d9 | ||
|
|
33b1ab9e79 | ||
|
|
7b3ca83329 | ||
|
|
76d8fa7b7e | ||
|
|
9141f0419a | ||
|
|
857fb399e1 | ||
|
|
b3129192ad | ||
|
|
b253440cf5 | ||
|
|
5429fca422 | ||
|
|
46e299d2b6 | ||
|
|
34601f7e05 | ||
|
|
cf70797083 | ||
|
|
07ba33b893 | ||
|
|
f818885636 | ||
|
|
738d1910da | ||
|
|
651efe248b | ||
|
|
768d4c0df3 | ||
|
|
8ceb9baf9a | ||
|
|
35ca313b48 | ||
|
|
1a78557b61 | ||
|
|
dafb627278 | ||
|
|
eeb01a139d | ||
|
|
4a8f0e9562 | ||
|
|
ea9d3cb5f7 | ||
|
|
3d15b097b7 | ||
|
|
5a07282d19 | ||
|
|
7e792b2b5e | ||
|
|
c3a94454e0 | ||
|
|
2f23e1fc0c | ||
|
|
996c71a933 | ||
|
|
7731f5943a | ||
|
|
bb49d5086c | ||
|
|
e168413e9f | ||
|
|
a59e3d0c57 | ||
|
|
f73da456fa | ||
|
|
9ccd6ecd74 |
3156
.github/fabricbot.json
vendored
Normal file
3156
.github/fabricbot.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,9 +2,9 @@
|
||||
trigger: none
|
||||
pr: none
|
||||
|
||||
pool:
|
||||
pool:
|
||||
name: WinDevPool-L
|
||||
demands: ImageOverride -equals WinDevVS16-latest
|
||||
demands: ImageOverride -equals WinDevVS17-latest
|
||||
|
||||
parameters:
|
||||
- name: branding
|
||||
@@ -195,7 +195,6 @@ jobs:
|
||||
condition: true
|
||||
inputs:
|
||||
solution: '**\OpenConsole.sln'
|
||||
vsVersion: 16.0
|
||||
msbuildArgs: /p:WindowsTerminalOfficialBuild=true /p:WindowsTerminalBranding=${{ parameters.branding }};PGOBuildMode=${{ parameters.pgoBuildMode }} /t:Terminal\CascadiaPackage /p:WindowsTerminalReleaseBuild=true /bl:$(Build.SourcesDirectory)\msbuild.binlog
|
||||
platform: $(BuildPlatform)
|
||||
configuration: $(BuildConfiguration)
|
||||
@@ -222,7 +221,6 @@ jobs:
|
||||
displayName: Build solution **\OpenConsole.sln for PublicTerminalCore
|
||||
inputs:
|
||||
solution: '**\OpenConsole.sln'
|
||||
vsVersion: 16.0
|
||||
msbuildArgs: /p:WindowsTerminalOfficialBuild=true /p:WindowsTerminalBranding=${{ parameters.branding }};PGOBuildMode=${{ parameters.pgoBuildMode }} /p:WindowsTerminalReleaseBuild=true /t:Terminal\wpf\PublicTerminalCore
|
||||
platform: $(BuildPlatform)
|
||||
configuration: $(BuildConfiguration)
|
||||
@@ -231,7 +229,6 @@ jobs:
|
||||
displayName: Build solution **\OpenConsole.sln for ConPTY
|
||||
inputs:
|
||||
solution: '**\OpenConsole.sln'
|
||||
vsVersion: 16.0
|
||||
msbuildArgs: /p:WindowsTerminalOfficialBuild=true /p:WindowsTerminalBranding=${{ parameters.branding }};PGOBuildMode=${{ parameters.pgoBuildMode }} /p:WindowsTerminalReleaseBuild=true /t:Conhost\Host_EXE;Conhost\winconpty_DLL
|
||||
platform: $(BuildPlatform)
|
||||
configuration: $(BuildConfiguration)
|
||||
@@ -327,7 +324,7 @@ jobs:
|
||||
|
||||
- ${{ if eq(parameters.runCompliance, true) }}:
|
||||
- template: ./templates/build-console-compliance-job.yml
|
||||
|
||||
|
||||
- ${{ if eq(parameters.buildTerminal, true) }}:
|
||||
- job: BundleAndSign
|
||||
strategy:
|
||||
@@ -546,7 +543,6 @@ jobs:
|
||||
displayName: Build solution **\OpenConsole.sln for WPF Control
|
||||
inputs:
|
||||
solution: '**\OpenConsole.sln'
|
||||
vsVersion: 16.0
|
||||
msbuildArgs: /p:WindowsTerminalReleaseBuild=$(UseReleaseBranding);Version=$(XES_PACKAGEVERSIONNUMBER) /t:Pack
|
||||
platform: Any CPU
|
||||
configuration: $(BuildConfiguration)
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
name: WinDevPoolOSS-L
|
||||
${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPool-L
|
||||
demands: ImageOverride -equals WinDevVS16-latest
|
||||
demands: ImageOverride -equals WinDevVS17-latest
|
||||
|
||||
steps:
|
||||
- checkout: self
|
||||
@@ -27,7 +27,6 @@ jobs:
|
||||
displayName: 'Build solution **\OpenConsole.sln'
|
||||
inputs:
|
||||
solution: '**\OpenConsole.sln'
|
||||
vsVersion: 16.0
|
||||
platform: '$(BuildPlatform)'
|
||||
configuration: '$(BuildConfiguration)'
|
||||
msbuildArgs: ${{ parameters.additionalBuildArguments }}
|
||||
|
||||
@@ -12,12 +12,12 @@ jobs:
|
||||
BuildPlatform: ${{ parameters.platform }}
|
||||
WindowsTerminalBranding: ${{ parameters.branding }}
|
||||
EnableRichCodeNavigation: true
|
||||
pool:
|
||||
pool:
|
||||
${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPoolOSS-L
|
||||
${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPool-L
|
||||
demands: ImageOverride -equals WinDevVS16-latest
|
||||
demands: ImageOverride -equals WinDevVS17-latest
|
||||
|
||||
steps:
|
||||
- template: build-console-steps.yml
|
||||
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
If ($Arch -Eq "x86") { $Arch = "Win32" }
|
||||
|
||||
Write-Host "##vso[task.setvariable variable=RationalizedBuildPlatform]${Arch}"
|
||||
- steps: restore-nuget-steps.yml
|
||||
- template: restore-nuget-steps.yml
|
||||
- task: UniversalPackages@0
|
||||
displayName: Download terminal-internal Universal Package
|
||||
inputs:
|
||||
|
||||
@@ -9,12 +9,12 @@ jobs:
|
||||
variables:
|
||||
BuildConfiguration: ${{ parameters.configuration }}
|
||||
BuildPlatform: ${{ parameters.platform }}
|
||||
pool:
|
||||
pool:
|
||||
${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPoolOSS-L
|
||||
${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPool-L
|
||||
demands: ImageOverride -equals WinDevVS16-latest
|
||||
demands: ImageOverride -equals WinDevVS17-latest
|
||||
|
||||
steps:
|
||||
- checkout: self
|
||||
@@ -32,12 +32,11 @@ jobs:
|
||||
echo VCToolsInstallDir = %VCToolsInstallDir%
|
||||
echo ##vso[task.setvariable variable=VCToolsInstallDir]%VCToolsInstallDir%
|
||||
displayName: 'Retrieve VC tools directory'
|
||||
|
||||
|
||||
- task: VSBuild@1
|
||||
displayName: 'Build solution **\OpenConsole.sln'
|
||||
inputs:
|
||||
solution: '**\OpenConsole.sln'
|
||||
vsVersion: 16.0
|
||||
platform: '$(BuildPlatform)'
|
||||
configuration: '$(BuildConfiguration)'
|
||||
msbuildArgs: "${{ parameters.additionalBuildArguments }}"
|
||||
@@ -88,4 +87,4 @@ jobs:
|
||||
displayName: 'Publish All Build Artifacts'
|
||||
inputs:
|
||||
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
|
||||
ArtifactName: 'fuzzingBuildOutput'
|
||||
ArtifactName: 'fuzzingBuildOutput'
|
||||
|
||||
@@ -12,12 +12,12 @@ jobs:
|
||||
BuildConfiguration: ${{ parameters.configuration }}
|
||||
BuildPlatform: ${{ parameters.platform }}
|
||||
PGOBuildMode: 'Instrument'
|
||||
pool:
|
||||
pool:
|
||||
${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPoolOSS-L
|
||||
${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPool-L
|
||||
demands: ImageOverride -equals WinDevVS16-latest
|
||||
demands: ImageOverride -equals WinDevVS17-latest
|
||||
|
||||
steps:
|
||||
- template: build-console-steps.yml
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
configuration: ${{ parameters.configuration }}
|
||||
platform: ${{ parameters.platform }}
|
||||
rerunPassesRequiredToAvoidFailure: ${{ parameters.rerunPassesRequiredToAvoidFailure }}
|
||||
|
||||
|
||||
- template: helix-processtestresults-job.yml
|
||||
parameters:
|
||||
name: 'ProcessTestResults'
|
||||
|
||||
@@ -28,7 +28,6 @@ steps:
|
||||
displayName: 'Build solution **\OpenConsole.sln'
|
||||
inputs:
|
||||
solution: '**\OpenConsole.sln'
|
||||
vsVersion: 16.0
|
||||
platform: '$(BuildPlatform)'
|
||||
configuration: '$(BuildConfiguration)'
|
||||
msbuildArgs: "${{ parameters.additionalBuildArguments }} /p:PGOBuildMode=$(PGOBuildMode) /bl:$(Build.SourcesDirectory)\\msbuild.binlog"
|
||||
|
||||
@@ -11,12 +11,12 @@ jobs:
|
||||
variables:
|
||||
BuildConfiguration: ${{ parameters.configuration }}
|
||||
BuildPlatform: ${{ parameters.platform }}
|
||||
pool:
|
||||
pool:
|
||||
${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPoolOSS-L
|
||||
${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPool-L
|
||||
demands: ImageOverride -equals WinDevVS16-latest
|
||||
demands: ImageOverride -equals WinDevVS17-latest
|
||||
|
||||
steps:
|
||||
- checkout: self
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<XesUseOneStoreVersioning>true</XesUseOneStoreVersioning>
|
||||
<XesBaseYearForStoreVersion>2022</XesBaseYearForStoreVersion>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>15</VersionMinor>
|
||||
<VersionMinor>16</VersionMinor>
|
||||
<VersionInfoProductName>Windows Terminal</VersionInfoProductName>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<!-- Managed packages -->
|
||||
<package id="Appium.WebDriver" version="3.0.0.2" targetFramework="net45" />
|
||||
<package id="Castle.Core" version="4.1.1" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net45" />
|
||||
<package id="Selenium.Support" version="3.5.0" targetFramework="net45" />
|
||||
<package id="Selenium.WebDriver" version="3.5.0" targetFramework="net45" />
|
||||
</packages>
|
||||
|
||||
@@ -110,7 +110,7 @@ This takes quite some time, and only generates an `msix`. It does not install th
|
||||
|
||||
```powershell
|
||||
# If you haven't already:
|
||||
Import-Module tools\OpenConsole.psm1;
|
||||
Import-Module .\tools\OpenConsole.psm1;
|
||||
Set-MsBuildDevEnvironment;
|
||||
|
||||
# The Set-MsBuildDevEnvironment call is needed for finding the path to
|
||||
@@ -121,7 +121,7 @@ if ((Get-AppxPackage -Name 'WindowsTerminalDev*') -ne $null) {
|
||||
Remove-AppxPackage 'WindowsTerminalDev_0.0.1.0_x64__8wekyb3d8bbwe'
|
||||
};
|
||||
New-Item ..\loose -Type Directory -Force;
|
||||
makeappx unpack /v /o /p .\CascadiaPackage_0.0.1.0_x64_Debug.msix /d ..\Loose\;
|
||||
makeappx unpack /v /o /p .\CascadiaPackage_0.0.1.0_x64_Debug.msix /d ..\loose\;
|
||||
Add-AppxPackage -Path ..\loose\AppxManifest.xml -Register -ForceUpdateFromAnyVersion -ForceApplicationShutdown
|
||||
```
|
||||
|
||||
|
||||
@@ -350,9 +350,11 @@
|
||||
"switchToTab",
|
||||
"tabSearch",
|
||||
"toggleAlwaysOnTop",
|
||||
"toggleBlockSelection",
|
||||
"toggleFocusMode",
|
||||
"selectAll",
|
||||
"setFocusMode",
|
||||
"switchSelectionEndpoint",
|
||||
"toggleFullscreen",
|
||||
"setFullScreen",
|
||||
"setMaximized",
|
||||
@@ -364,6 +366,10 @@
|
||||
"quit",
|
||||
"adjustOpacity",
|
||||
"restoreLastClosed",
|
||||
"addMark",
|
||||
"scrollToMark",
|
||||
"clearMark",
|
||||
"clearAllMarks",
|
||||
"unbound"
|
||||
],
|
||||
"type": "string"
|
||||
@@ -383,6 +389,15 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"ScrollToMarkDirection": {
|
||||
"enum": [
|
||||
"previous",
|
||||
"next",
|
||||
"first",
|
||||
"last"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"ResizeDirection": {
|
||||
"enum": [
|
||||
"left",
|
||||
@@ -732,6 +747,30 @@
|
||||
"direction"
|
||||
]
|
||||
},
|
||||
"ScrollToMarkAction": {
|
||||
"description": "Arguments corresponding to a Scroll to Mark Action",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/ShortcutAction"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"action": {
|
||||
"type": "string",
|
||||
"const": "scrollToMark"
|
||||
},
|
||||
"direction": {
|
||||
"$ref": "#/$defs/ScrollToMarkDirection",
|
||||
"default": "previous",
|
||||
"description": "The direction to scroll to a mark."
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": [
|
||||
"direction"
|
||||
]
|
||||
},
|
||||
"SendInputAction": {
|
||||
"description": "Arguments corresponding to a Send Input Action",
|
||||
"allOf": [
|
||||
@@ -839,6 +878,27 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"AddMarkAction": {
|
||||
"description": "Arguments corresponding to an Add Mark Action",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/ShortcutAction"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"action": {
|
||||
"type": "string",
|
||||
"const": "addMark"
|
||||
},
|
||||
"color": {
|
||||
"$ref": "#/$defs/Color",
|
||||
"default": null,
|
||||
"description": "If provided, will set the mark's color to the given value."
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"SetColorSchemeAction": {
|
||||
"description": "Arguments corresponding to a Set Color Scheme Action",
|
||||
"allOf": [
|
||||
@@ -1667,6 +1727,16 @@
|
||||
"description": "When set to true, URLs will be detected by the Terminal. This will cause URLs to underline on hover and be clickable by pressing Ctrl.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"experimental.autoMarkPrompts": {
|
||||
"default": false,
|
||||
"description": "When set to true, prompts will automatically be marked.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"experimental.showMarksOnScrollbar": {
|
||||
"default": false,
|
||||
"description": "When set to true, marks added to the buffer via the addMark action will appear on the scrollbar.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"disableAnimations": {
|
||||
"default": false,
|
||||
"description": "When set to `true`, visual animations will be disabled across the application.",
|
||||
@@ -2011,6 +2081,10 @@
|
||||
"description": "Controls what happens when the application emits a BEL character. When set to \"all\", the Terminal will play a sound, flash the taskbar icon (if the terminal window is not in focus) and flash the window. An array of specific behaviors can also be used. Supported array values include `audible`, `window` and `taskbar`. When set to \"none\", nothing will happen.",
|
||||
"$ref": "#/$defs/BellStyle"
|
||||
},
|
||||
"bellSound": {
|
||||
"description": "Sets the sound played when the application emits a BEL. When set to an array, the terminal will pick one of those sounds at random.",
|
||||
"$ref": "#/$defs/BellSound"
|
||||
},
|
||||
"closeOnExit": {
|
||||
"default": "graceful",
|
||||
"description": "Sets how the profile reacts to termination or failure to launch. Possible values:\n -\"graceful\" (close when exit is typed or the process exits normally)\n -\"always\" (always close)\n -\"never\" (never close).\ntrue and false are accepted as synonyms for \"graceful\" and \"never\" respectively.",
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
|
||||
#include "pch.h"
|
||||
#include "MyPage.h"
|
||||
#include "MySettings.h"
|
||||
#include <LibraryResources.h>
|
||||
#include "MyPage.g.cpp"
|
||||
#include "MySettings.h"
|
||||
#include "..\..\..\src\cascadia\UnitTests_Control\MockControlSettings.h"
|
||||
#include "..\..\..\src\types\inc\utils.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
@@ -26,7 +28,7 @@ namespace winrt::SampleApp::implementation
|
||||
|
||||
void MyPage::Create()
|
||||
{
|
||||
auto settings = winrt::make_self<implementation::MySettings>();
|
||||
auto settings = winrt::make_self<MySettings>();
|
||||
|
||||
auto connectionSettings{ TerminalConnection::ConptyConnection::CreateSettings(L"cmd.exe /k echo This TermControl is hosted in-proc...",
|
||||
winrt::hstring{},
|
||||
@@ -44,6 +46,216 @@ namespace winrt::SampleApp::implementation
|
||||
Control::TermControl control{ *settings, *settings, conn };
|
||||
|
||||
InProcContent().Children().Append(control);
|
||||
|
||||
// Once the control loads (and not before that), write some text for debugging:
|
||||
control.Initialized([conn](auto&&, auto&&) {
|
||||
conn.WriteInput(L"This TermControl is hosted in-proc...");
|
||||
});
|
||||
}
|
||||
|
||||
static wil::unique_process_information _createHostClassProcess(const winrt::guid& g)
|
||||
{
|
||||
auto guidStr{ ::Microsoft::Console::Utils::GuidToString(g) };
|
||||
|
||||
// Create an event that the content process will use to signal it is
|
||||
// ready to go. We won't need the event after this function, so the
|
||||
// unique_event will clean up our handle when we leave this scope. The
|
||||
// ContentProcess is responsible for cleaning up its own handle.
|
||||
wil::unique_event ev{ CreateEvent(nullptr, true, false, nullptr) };
|
||||
// Make sure to mark this handle as inheritable! Even with
|
||||
// bInheritHandles=true, this is only inherited when it's explicitly
|
||||
// allowed to be.
|
||||
SetHandleInformation(ev.get(), HANDLE_FLAG_INHERIT, 1);
|
||||
|
||||
// god bless, fmt::format will format a HANDLE like `0xa80`
|
||||
std::wstring commandline{
|
||||
fmt::format(L"WindowsTerminal.exe --content {} --signal {}", guidStr, ev.get())
|
||||
};
|
||||
|
||||
STARTUPINFO siOne{ 0 };
|
||||
siOne.cb = sizeof(STARTUPINFOW);
|
||||
wil::unique_process_information piOne;
|
||||
auto succeeded = CreateProcessW(
|
||||
nullptr,
|
||||
commandline.data(),
|
||||
nullptr, // lpProcessAttributes
|
||||
nullptr, // lpThreadAttributes
|
||||
true, // bInheritHandles
|
||||
CREATE_UNICODE_ENVIRONMENT, // dwCreationFlags
|
||||
nullptr, // lpEnvironment
|
||||
nullptr, // startingDirectory
|
||||
&siOne, // lpStartupInfo
|
||||
&piOne // lpProcessInformation
|
||||
);
|
||||
THROW_IF_WIN32_BOOL_FALSE(succeeded);
|
||||
|
||||
// Wait for the child process to signal that they're ready.
|
||||
WaitForSingleObject(ev.get(), INFINITE);
|
||||
|
||||
return piOne;
|
||||
}
|
||||
|
||||
winrt::fire_and_forget MyPage::_writeToLog(std::wstring_view str)
|
||||
{
|
||||
winrt::hstring copy{ str };
|
||||
// Switch back to the UI thread.
|
||||
co_await resume_foreground(Dispatcher());
|
||||
winrt::WUX::Controls::TextBlock block;
|
||||
block.Text(copy);
|
||||
Log().Children().Append(block);
|
||||
}
|
||||
|
||||
winrt::fire_and_forget MyPage::CreateClicked(const IInspectable& sender,
|
||||
const WUX::Input::TappedRoutedEventArgs& eventArgs)
|
||||
{
|
||||
auto guidString = GuidInput().Text();
|
||||
|
||||
// Capture calling context.
|
||||
winrt::apartment_context ui_thread;
|
||||
|
||||
auto canConvert = guidString.size() == 38 &&
|
||||
guidString.front() == '{' &&
|
||||
guidString.back() == '}';
|
||||
bool tryingToAttach = false;
|
||||
winrt::guid contentGuid{ ::Microsoft::Console::Utils::CreateGuid() };
|
||||
|
||||
if (canConvert)
|
||||
{
|
||||
GUID result{};
|
||||
if (SUCCEEDED(IIDFromString(guidString.c_str(), &result)))
|
||||
{
|
||||
contentGuid = result;
|
||||
tryingToAttach = true;
|
||||
}
|
||||
}
|
||||
_writeToLog(tryingToAttach ? L"Attaching to existing content process" : L"Creating new content process");
|
||||
|
||||
co_await winrt::resume_background();
|
||||
if (!tryingToAttach)
|
||||
{
|
||||
// Spawn a wt.exe, with the guid on the commandline
|
||||
piContentProcess = std::move(_createHostClassProcess(contentGuid));
|
||||
}
|
||||
|
||||
// THIS MUST TAKE PLACE AFTER _createHostClassProcess.
|
||||
// * If we're creating a new OOP control, _createHostClassProcess will
|
||||
// spawn the process that will actually host the ContentProcess
|
||||
// object.
|
||||
// * If we're attaching, then that process already exists.
|
||||
Control::ContentProcess content{ nullptr };
|
||||
try
|
||||
{
|
||||
content = create_instance<Control::ContentProcess>(contentGuid, CLSCTX_LOCAL_SERVER);
|
||||
}
|
||||
catch (winrt::hresult_error hr)
|
||||
{
|
||||
_writeToLog(L"CreateInstance the ContentProcess object");
|
||||
_writeToLog(fmt::format(L" HR ({}): {}", hr.code(), hr.message().c_str()));
|
||||
co_return; // be sure to co_return or we'll fall through to the part where we clear the log
|
||||
}
|
||||
|
||||
if (content == nullptr)
|
||||
{
|
||||
_writeToLog(L"Failed to connect to the ContentProcess object. It may not have been started fast enough.");
|
||||
co_return; // be sure to co_return or we'll fall through to the part where we clear the log
|
||||
}
|
||||
|
||||
TerminalConnection::ConnectionInformation connectInfo{ nullptr };
|
||||
Control::IControlSettings settings{ *winrt::make_self<implementation::MySettings>() };
|
||||
|
||||
// When creating a terminal for the first time, pass it a connection
|
||||
// info
|
||||
//
|
||||
// otherwise, when attaching to an existing one, just pass null, because
|
||||
// we don't need the connection info.
|
||||
if (!tryingToAttach)
|
||||
{
|
||||
auto connectionSettings{ TerminalConnection::ConptyConnection::CreateSettings(L"cmd.exe /k echo This TermControl is hosted out-of-proc...",
|
||||
winrt::hstring{},
|
||||
L"",
|
||||
nullptr,
|
||||
32,
|
||||
80,
|
||||
winrt::guid()) };
|
||||
|
||||
// "Microsoft.Terminal.TerminalConnection.ConptyConnection"
|
||||
winrt::hstring myClass{ winrt::name_of<TerminalConnection::ConptyConnection>() };
|
||||
connectInfo = TerminalConnection::ConnectionInformation(myClass, connectionSettings);
|
||||
|
||||
if (!content.Initialize(settings, settings, connectInfo))
|
||||
{
|
||||
_writeToLog(L"Failed to Initialize the ContentProcess object.");
|
||||
co_return; // be sure to co_return or we'll fall through to the part where we clear the log
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we're attaching, we don't really need to do anything special.
|
||||
}
|
||||
|
||||
// Switch back to the UI thread.
|
||||
co_await ui_thread;
|
||||
|
||||
// Create the XAML control that will be attached to the content process.
|
||||
// We're not passing in a connection, because the contentGuid will be used instead.
|
||||
Control::TermControl control{ contentGuid, settings, settings, nullptr };
|
||||
auto weakControl = winrt::make_weak(control);
|
||||
control.RaiseNotice([this](auto&&, auto& args) {
|
||||
_writeToLog(L"Content process died, probably.");
|
||||
_writeToLog(args.Message());
|
||||
OutOfProcContent().Children().Clear();
|
||||
GuidInput().Text(L"");
|
||||
if (piContentProcess.hProcess)
|
||||
{
|
||||
piContentProcess.reset();
|
||||
}
|
||||
});
|
||||
control.ConnectionStateChanged([this, weakControl](auto&&, auto&) {
|
||||
if (auto strongControl{ weakControl.get() })
|
||||
{
|
||||
const auto newConnectionState = strongControl.ConnectionState();
|
||||
if (newConnectionState == TerminalConnection::ConnectionState::Closed)
|
||||
{
|
||||
_writeToLog(L"Connection was closed");
|
||||
OutOfProcContent().Children().Clear();
|
||||
GuidInput().Text(L"");
|
||||
if (piContentProcess.hProcess)
|
||||
{
|
||||
piContentProcess.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Log().Children().Clear();
|
||||
OutOfProcContent().Children().Append(control);
|
||||
|
||||
if (!tryingToAttach)
|
||||
{
|
||||
auto guidStr{ ::Microsoft::Console::Utils::GuidToString(contentGuid) };
|
||||
GuidInput().Text(guidStr);
|
||||
}
|
||||
}
|
||||
|
||||
void MyPage::CloseClicked(const IInspectable& /*sender*/,
|
||||
const WUX::Input::TappedRoutedEventArgs& /*eventArgs*/)
|
||||
{
|
||||
OutOfProcContent().Children().Clear();
|
||||
GuidInput().Text(L"");
|
||||
if (piContentProcess.hProcess)
|
||||
{
|
||||
piContentProcess.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void MyPage::KillClicked(const IInspectable& /*sender*/,
|
||||
const WUX::Input::TappedRoutedEventArgs& /*eventArgs*/)
|
||||
{
|
||||
if (piContentProcess.hProcess)
|
||||
{
|
||||
TerminateProcess(piContentProcess.hProcess, (UINT)-1);
|
||||
piContentProcess.reset();
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -14,11 +14,18 @@ namespace winrt::SampleApp::implementation
|
||||
MyPage();
|
||||
|
||||
void Create();
|
||||
|
||||
hstring Title();
|
||||
|
||||
winrt::fire_and_forget CreateClicked(const IInspectable& sender, const Windows::UI::Xaml::Input::TappedRoutedEventArgs& eventArgs);
|
||||
void CloseClicked(const IInspectable& sender, const Windows::UI::Xaml::Input::TappedRoutedEventArgs& eventArgs);
|
||||
void KillClicked(const IInspectable& sender, const Windows::UI::Xaml::Input::TappedRoutedEventArgs& eventArgs);
|
||||
|
||||
private:
|
||||
friend struct MyPageT<MyPage>; // for Xaml to bind events
|
||||
|
||||
wil::unique_process_information piContentProcess;
|
||||
|
||||
winrt::fire_and_forget _writeToLog(std::wstring_view str);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -23,9 +23,23 @@
|
||||
<TextBox x:Name="GuidInput"
|
||||
Width="400"
|
||||
PlaceholderText="{}{guid here}" />
|
||||
<Button Grid.Row="0">
|
||||
<Button x:Name="CreateOutOfProcControl"
|
||||
Grid.Row="0"
|
||||
Tapped="CreateClicked">
|
||||
Create
|
||||
</Button>
|
||||
<Button x:Name="CloseOutOfProcControl"
|
||||
Grid.Row="0"
|
||||
Margin="4,0,0,0"
|
||||
Tapped="CloseClicked">
|
||||
Close
|
||||
</Button>
|
||||
<Button x:Name="KillOutOfProcControl"
|
||||
Grid.Row="0"
|
||||
Margin="4,0,0,0"
|
||||
Tapped="KillClicked">
|
||||
Kill
|
||||
</Button>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
@@ -46,14 +60,26 @@
|
||||
VerticalAlignment="Stretch"
|
||||
Background="#ff0000" />
|
||||
|
||||
<Grid x:Name="OutOfProcContent"
|
||||
Grid.Column="1"
|
||||
Padding="16"
|
||||
<Grid Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Background="#0000ff" />
|
||||
VerticalAlignment="Stretch">
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel x:Name="Log"
|
||||
Grid.Row="0"
|
||||
Orientation="Vertical" />
|
||||
|
||||
<Grid x:Name="OutOfProcContent"
|
||||
Grid.Row="1"
|
||||
Padding="16"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Background="#0000ff" />
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
|
||||
|
||||
@@ -17,9 +17,15 @@
|
||||
<DisableEmbeddedXbf>false</DisableEmbeddedXbf>
|
||||
<XamlComponentResourceLocation>nested</XamlComponentResourceLocation>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="NuGet Dependencies">
|
||||
<TerminalCppWinrt>true</TerminalCppWinrt>
|
||||
<TerminalXamlApplicationToolkit>true</TerminalXamlApplicationToolkit>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
|
||||
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props')" />
|
||||
|
||||
<ItemDefinitionGroup>
|
||||
|
||||
<ClCompile>
|
||||
@@ -147,14 +153,15 @@
|
||||
<!-- ========================= Globals ======================== -->
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
|
||||
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" />
|
||||
<!-- This -must- go after cppwinrt.build.post.props because that includes many VS-provided props including appcontainer.common.props, which stomps on what cppwinrt.targets did. -->
|
||||
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
|
||||
|
||||
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" />
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
</Target>
|
||||
|
||||
<!--
|
||||
|
||||
@@ -11,9 +11,13 @@
|
||||
<!-- sets a bunch of Windows Universal properties -->
|
||||
<OpenConsoleUniversalApp>true</OpenConsoleUniversalApp>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="NuGet Dependencies">
|
||||
<TerminalCppWinrt>true</TerminalCppWinrt>
|
||||
<TerminalXamlApplicationToolkit>true</TerminalXamlApplicationToolkit>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
|
||||
<Import Project="$(OpenConsoleDir)packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props')" />
|
||||
<!-- ========================= XAML files ======================== -->
|
||||
<ItemGroup>
|
||||
<!-- DON'T PUT XAML FILES HERE! Put them in SampleAppLib.vcxproj -->
|
||||
@@ -81,13 +85,11 @@
|
||||
|
||||
|
||||
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Import Project="$(OpenConsoleDir)packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" />
|
||||
</Target>
|
||||
|
||||
<ItemDefinitionGroup>
|
||||
@@ -102,4 +104,7 @@
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
|
||||
|
||||
<!-- This -must- go after cppwinrt.build.post.props because that includes many VS-provided props including appcontainer.common.props, which stomps on what cppwinrt.targets did. -->
|
||||
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
|
||||
</Project>
|
||||
|
||||
@@ -57,8 +57,8 @@ void SampleIslandWindow::MakeWindow() noexcept
|
||||
WS_OVERLAPPEDWINDOW,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
1024,
|
||||
860,
|
||||
nullptr,
|
||||
nullptr,
|
||||
wc.hInstance,
|
||||
@@ -104,8 +104,6 @@ void SampleIslandWindow::_HandleCreateWindow(const WPARAM, const LPARAM lParam)
|
||||
|
||||
void SampleIslandWindow::Initialize()
|
||||
{
|
||||
const bool initialized = (_interopWindowHandle != nullptr);
|
||||
|
||||
_source = DesktopWindowXamlSource{};
|
||||
|
||||
auto interop = _source.as<IDesktopWindowXamlSourceNative>();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props')" />
|
||||
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{b4427499-9fde-4208-b456-5bc580637633}</ProjectGuid>
|
||||
@@ -16,7 +15,15 @@
|
||||
<TargetPlatformIdentifier>Windows</TargetPlatformIdentifier>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="NuGet Dependencies">
|
||||
<TerminalCppWinrt>true</TerminalCppWinrt>
|
||||
<TerminalXamlApplicationToolkit>true</TerminalXamlApplicationToolkit>
|
||||
<TerminalVCRTForwarders>true</TerminalVCRTForwarders>
|
||||
<TerminalThemeHelpers>true</TerminalThemeHelpers>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
|
||||
|
||||
<ItemDefinitionGroup>
|
||||
@@ -138,16 +145,11 @@
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
|
||||
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.4\build\native\Microsoft.VCRTForwarders.140.targets" Condition="Exists('..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.4\build\native\Microsoft.VCRTForwarders.140.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.4\build\native\Microsoft.VCRTForwarders.140.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.4\build\native\Microsoft.VCRTForwarders.140.targets'))" />
|
||||
</Target>
|
||||
|
||||
<!-- Override GetPackagingOutputs to roll up all our dependencies.
|
||||
@@ -225,13 +227,24 @@
|
||||
<TargetPath>%(Filename)%(Extension)</TargetPath>
|
||||
</PackagingOutputs>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Same thing AGAIN here, with OpenConsole.exe If you forget this, then
|
||||
the scratch app will use the inbox conpty with a newer conpty lib, causing
|
||||
us to send the inbox conhost messages that will make it explode. -->
|
||||
<ItemGroup>
|
||||
<_OpenConsoleExe Include="$(OpenConsoleCommonOutDir)\OpenConsole.exe" />
|
||||
|
||||
<PackagingOutputs Include="@(_OpenConsoleExe)">
|
||||
<ProjectName>$(ProjectName)</ProjectName>
|
||||
<OutputGroup>BuiltProjectOutputGroup</OutputGroup>
|
||||
<TargetPath>%(Filename)%(Extension)</TargetPath>
|
||||
</PackagingOutputs>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<Import Project="$(OpenConsoleDir)\build\rules\GenerateSxsManifestsFromWinmds.targets" />
|
||||
<Import Project="..\..\..\packages\Terminal.ThemeHelpers.0.2.200324001\build\native\Terminal.ThemeHelpers.targets" Condition="Exists('..\..\..\packages\Terminal.ThemeHelpers.0.2.200324001\build\native\Terminal.ThemeHelpers.targets')" />
|
||||
|
||||
<!-- This -must- go after cppwinrt.build.post.props because that includes many VS-provided props including appcontainer.common.props, which stomps on what cppwinrt.targets did. -->
|
||||
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
|
||||
</Project>
|
||||
|
||||
|
||||
2
src/audio/dirs
Normal file
2
src/audio/dirs
Normal file
@@ -0,0 +1,2 @@
|
||||
DIRS=midi \
|
||||
|
||||
2
src/audio/midi/dirs
Normal file
2
src/audio/midi/dirs
Normal file
@@ -0,0 +1,2 @@
|
||||
DIRS=lib \
|
||||
|
||||
8
src/audio/midi/lib/sources
Normal file
8
src/audio/midi/lib/sources
Normal file
@@ -0,0 +1,8 @@
|
||||
!include ..\sources.inc
|
||||
|
||||
# -------------------------------------
|
||||
# Program Information
|
||||
# -------------------------------------
|
||||
|
||||
TARGETNAME = ConAudioMidi
|
||||
TARGETTYPE = LIBRARY
|
||||
@@ -26,5 +26,6 @@ Abstract:
|
||||
|
||||
// Windows Header Files:
|
||||
#include <windows.h>
|
||||
#include <mmeapi.h>
|
||||
|
||||
// clang-format on
|
||||
|
||||
28
src/audio/midi/sources.inc
Normal file
28
src/audio/midi/sources.inc
Normal file
@@ -0,0 +1,28 @@
|
||||
!include ..\..\..\project.inc
|
||||
|
||||
# -------------------------------------
|
||||
# Windows Console
|
||||
# - Console Audio Functions
|
||||
# -------------------------------------
|
||||
|
||||
# -------------------------------------
|
||||
# Build System Settings
|
||||
# -------------------------------------
|
||||
|
||||
# Code in the OneCore depot automatically excludes default Win32 libraries.
|
||||
|
||||
# -------------------------------------
|
||||
# Sources, Headers, and Libraries
|
||||
# -------------------------------------
|
||||
|
||||
PRECOMPILED_CXX = 1
|
||||
PRECOMPILED_INCLUDE = ..\precomp.h
|
||||
|
||||
SOURCES = \
|
||||
..\MidiAudio.cpp \
|
||||
|
||||
INCLUDES = \
|
||||
$(INCLUDES); \
|
||||
..; \
|
||||
..\..\..\inc; \
|
||||
$(MINWIN_INTERNAL_PRIV_SDK_INC_PATH_L); \
|
||||
@@ -1138,7 +1138,7 @@ til::point TextBuffer::GetWordStart(const til::point target, const std::wstring_
|
||||
// that it actually points to a space in the buffer
|
||||
copy = bufferSize.BottomRightInclusive();
|
||||
}
|
||||
else if (bufferSize.CompareInBounds(target, limit, true) >= 0)
|
||||
else if (target >= limit)
|
||||
{
|
||||
// if at/past the limit --> clamp to limit
|
||||
copy = limitOptional.value_or(bufferSize.BottomRightInclusive());
|
||||
@@ -1254,7 +1254,7 @@ til::point TextBuffer::GetWordEnd(const til::point target, const std::wstring_vi
|
||||
// Already at/past the limit. Can't move forward.
|
||||
const auto bufferSize{ GetSize() };
|
||||
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
|
||||
if (bufferSize.CompareInBounds(target, limit, true) >= 0)
|
||||
if (target >= limit)
|
||||
{
|
||||
return target;
|
||||
}
|
||||
@@ -1282,7 +1282,7 @@ til::point TextBuffer::_GetWordEndForAccessibility(const til::point target, cons
|
||||
const auto bufferSize{ GetSize() };
|
||||
auto result{ target };
|
||||
|
||||
if (bufferSize.CompareInBounds(target, limit, true) >= 0)
|
||||
if (target >= limit)
|
||||
{
|
||||
// if we're already on/past the last RegularChar,
|
||||
// clamp result to that position
|
||||
@@ -1419,7 +1419,7 @@ bool TextBuffer::MoveToNextWord(til::point& pos, const std::wstring_view wordDel
|
||||
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
|
||||
const auto copy{ _GetWordEndForAccessibility(pos, wordDelimiters, limit) };
|
||||
|
||||
if (bufferSize.CompareInBounds(copy, limit, true) >= 0)
|
||||
if (copy >= limit)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -1466,7 +1466,7 @@ til::point TextBuffer::GetGlyphStart(const til::point pos, std::optional<til::po
|
||||
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
|
||||
|
||||
// Clamp pos to limit
|
||||
if (bufferSize.CompareInBounds(resultPos, limit, true) > 0)
|
||||
if (resultPos > limit)
|
||||
{
|
||||
resultPos = limit;
|
||||
}
|
||||
@@ -1494,7 +1494,7 @@ til::point TextBuffer::GetGlyphEnd(const til::point pos, bool accessibilityMode,
|
||||
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
|
||||
|
||||
// Clamp pos to limit
|
||||
if (bufferSize.CompareInBounds(resultPos, limit, true) > 0)
|
||||
if (resultPos > limit)
|
||||
{
|
||||
resultPos = limit;
|
||||
}
|
||||
@@ -1524,9 +1524,19 @@ til::point TextBuffer::GetGlyphEnd(const til::point pos, bool accessibilityMode,
|
||||
bool TextBuffer::MoveToNextGlyph(til::point& pos, bool allowExclusiveEnd, std::optional<til::point> limitOptional) const
|
||||
{
|
||||
const auto bufferSize = GetSize();
|
||||
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
|
||||
bool pastEndInclusive;
|
||||
til::point limit;
|
||||
{
|
||||
// if the limit is past the end of the buffer,
|
||||
// 1) clamp limit to end of buffer
|
||||
// 2) set pastEndInclusive
|
||||
const auto endInclusive{ bufferSize.BottomRightInclusive() };
|
||||
const auto val = limitOptional.value_or(endInclusive);
|
||||
pastEndInclusive = val > endInclusive;
|
||||
limit = pastEndInclusive ? endInclusive : val;
|
||||
}
|
||||
|
||||
const auto distanceToLimit{ bufferSize.CompareInBounds(pos, limit, true) };
|
||||
const auto distanceToLimit{ bufferSize.CompareInBounds(pos, limit) + (pastEndInclusive ? 1 : 0) };
|
||||
if (distanceToLimit >= 0)
|
||||
{
|
||||
// Corner Case: we're on/past the limit
|
||||
@@ -1569,7 +1579,7 @@ bool TextBuffer::MoveToPreviousGlyph(til::point& pos, std::optional<til::point>
|
||||
const auto bufferSize = GetSize();
|
||||
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
|
||||
|
||||
if (bufferSize.CompareInBounds(pos, limit, true) > 0)
|
||||
if (pos > limit)
|
||||
{
|
||||
// we're past the end
|
||||
// clamp us to the limit
|
||||
@@ -1611,7 +1621,7 @@ const std::vector<til::inclusive_rect> TextBuffer::GetTextRects(til::point start
|
||||
// (0,0) is the top-left of the screen
|
||||
// the physically "higher" coordinate is closer to the top-left
|
||||
// the physically "lower" coordinate is closer to the bottom-right
|
||||
const auto [higherCoord, lowerCoord] = bufferSize.CompareInBounds(start, end) <= 0 ?
|
||||
const auto [higherCoord, lowerCoord] = start <= end ?
|
||||
std::make_tuple(start, end) :
|
||||
std::make_tuple(end, start);
|
||||
|
||||
|
||||
@@ -135,6 +135,10 @@
|
||||
<!-- 16.3.0 - remove non-resources.pri PRI files since we just forced them back in. -->
|
||||
<AppxPackagePayload Remove="@(AppxPackagePayload)" Condition="'%(Extension)' == '.pri' and '%(Filename)' != 'resources'" />
|
||||
<AppxUploadPackagePayload Remove="@(AppxUploadPackagePayload)" Condition="'%(Extension)' == '.pri' and '%(Filename)' != 'resources'" />
|
||||
|
||||
<!-- Remove all of the xaml files, because we are using embedded xbf payloads (saves about 500kb on disk!) -->
|
||||
<AppxPackagePayload Remove="@(AppxPackagePayload)" Condition="'%(Extension)' == '.xaml'" />
|
||||
<AppxUploadPackagePayload Remove="@(AppxUploadPackagePayload)" Condition="'%(Extension)' == '.xaml'" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
<ClCompile Include="DeserializationTests.cpp" />
|
||||
<ClCompile Include="SerializationTests.cpp" />
|
||||
<ClCompile Include="TerminalSettingsTests.cpp" />
|
||||
<ClCompile Include="ThemeTests.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
|
||||
281
src/cascadia/LocalTests_SettingsModel/ThemeTests.cpp
Normal file
281
src/cascadia/LocalTests_SettingsModel/ThemeTests.cpp
Normal file
@@ -0,0 +1,281 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include "../TerminalSettingsModel/Theme.h"
|
||||
#include "../TerminalSettingsModel/CascadiaSettings.h"
|
||||
#include "../types/inc/colorTable.hpp"
|
||||
#include "JsonTestClass.h"
|
||||
|
||||
#include <defaults.h>
|
||||
|
||||
using namespace Microsoft::Console;
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model::implementation;
|
||||
using namespace WEX::Logging;
|
||||
using namespace WEX::TestExecution;
|
||||
using namespace WEX::Common;
|
||||
|
||||
namespace SettingsModelLocalTests
|
||||
{
|
||||
// TODO:microsoft/terminal#3838:
|
||||
// Unfortunately, these tests _WILL NOT_ work in our CI. We're waiting for
|
||||
// an updated TAEF that will let us install framework packages when the test
|
||||
// package is deployed. Until then, these tests won't deploy in CI.
|
||||
|
||||
class ThemeTests : public JsonTestClass
|
||||
{
|
||||
// Use a custom AppxManifest to ensure that we can activate winrt types
|
||||
// from our test. This property will tell taef to manually use this as
|
||||
// the AppxManifest for this test class.
|
||||
// This does not yet work for anything XAML-y. See TabTests.cpp for more
|
||||
// details on that.
|
||||
BEGIN_TEST_CLASS(ThemeTests)
|
||||
TEST_CLASS_PROPERTY(L"RunAs", L"UAP")
|
||||
TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml")
|
||||
END_TEST_CLASS()
|
||||
|
||||
TEST_METHOD(ParseSimpleTheme);
|
||||
TEST_METHOD(ParseEmptyTheme);
|
||||
TEST_METHOD(ParseNoWindowTheme);
|
||||
TEST_METHOD(ParseNullWindowTheme);
|
||||
TEST_METHOD(ParseThemeWithNullThemeColor);
|
||||
TEST_METHOD(InvalidCurrentTheme);
|
||||
|
||||
static Core::Color rgb(uint8_t r, uint8_t g, uint8_t b) noexcept
|
||||
{
|
||||
return Core::Color{ r, g, b, 255 };
|
||||
}
|
||||
static Core::Color rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
|
||||
{
|
||||
return Core::Color{ r, g, b, a };
|
||||
}
|
||||
};
|
||||
|
||||
void ThemeTests::ParseSimpleTheme()
|
||||
{
|
||||
static constexpr std::string_view orangeTheme{ R"({
|
||||
"name": "orange",
|
||||
"tabRow":
|
||||
{
|
||||
"background": "#FFFF8800",
|
||||
"unfocusedBackground": "#FF8844"
|
||||
},
|
||||
"window":
|
||||
{
|
||||
"applicationTheme": "light",
|
||||
"useMica": true
|
||||
}
|
||||
})" };
|
||||
|
||||
const auto schemeObject = VerifyParseSucceeded(orangeTheme);
|
||||
auto theme = Theme::FromJson(schemeObject);
|
||||
VERIFY_ARE_EQUAL(L"orange", theme->Name());
|
||||
|
||||
VERIFY_IS_NOT_NULL(theme->TabRow());
|
||||
VERIFY_IS_NOT_NULL(theme->TabRow().Background());
|
||||
VERIFY_ARE_EQUAL(Settings::Model::ThemeColorType::Color, theme->TabRow().Background().ColorType());
|
||||
VERIFY_ARE_EQUAL(rgba(0xff, 0xff, 0x88, 0x00), theme->TabRow().Background().Color());
|
||||
VERIFY_ARE_EQUAL(rgba(0xff, 0x88, 0x44, 0xff), theme->TabRow().UnfocusedBackground().Color());
|
||||
|
||||
VERIFY_IS_NOT_NULL(theme->Window());
|
||||
VERIFY_ARE_EQUAL(winrt::Windows::UI::Xaml::ElementTheme::Light, theme->Window().RequestedTheme());
|
||||
VERIFY_ARE_EQUAL(true, theme->Window().UseMica());
|
||||
}
|
||||
|
||||
void ThemeTests::ParseEmptyTheme()
|
||||
{
|
||||
Log::Comment(L"This theme doesn't have any elements defined.");
|
||||
static constexpr std::string_view emptyTheme{ R"({
|
||||
"name": "empty"
|
||||
})" };
|
||||
|
||||
const auto schemeObject = VerifyParseSucceeded(emptyTheme);
|
||||
auto theme = Theme::FromJson(schemeObject);
|
||||
VERIFY_ARE_EQUAL(L"empty", theme->Name());
|
||||
VERIFY_IS_NULL(theme->TabRow());
|
||||
VERIFY_IS_NULL(theme->Window());
|
||||
VERIFY_ARE_EQUAL(winrt::Windows::UI::Xaml::ElementTheme::Default, theme->RequestedTheme());
|
||||
}
|
||||
|
||||
void ThemeTests::ParseNoWindowTheme()
|
||||
{
|
||||
Log::Comment(L"This theme doesn't have a window defined.");
|
||||
static constexpr std::string_view emptyTheme{ R"({
|
||||
"name": "noWindow",
|
||||
"tabRow":
|
||||
{
|
||||
"background": "#112233",
|
||||
"unfocusedBackground": "#FF884400"
|
||||
},
|
||||
})" };
|
||||
|
||||
const auto schemeObject = VerifyParseSucceeded(emptyTheme);
|
||||
auto theme = Theme::FromJson(schemeObject);
|
||||
VERIFY_ARE_EQUAL(L"noWindow", theme->Name());
|
||||
|
||||
VERIFY_IS_NOT_NULL(theme->TabRow());
|
||||
VERIFY_IS_NOT_NULL(theme->TabRow().Background());
|
||||
VERIFY_ARE_EQUAL(Settings::Model::ThemeColorType::Color, theme->TabRow().Background().ColorType());
|
||||
VERIFY_ARE_EQUAL(rgb(0x11, 0x22, 0x33), theme->TabRow().Background().Color());
|
||||
|
||||
VERIFY_IS_NULL(theme->Window());
|
||||
VERIFY_ARE_EQUAL(winrt::Windows::UI::Xaml::ElementTheme::Default, theme->RequestedTheme());
|
||||
}
|
||||
|
||||
void ThemeTests::ParseNullWindowTheme()
|
||||
{
|
||||
Log::Comment(L"This theme doesn't have a window defined.");
|
||||
static constexpr std::string_view emptyTheme{ R"({
|
||||
"name": "nullWindow",
|
||||
"tabRow":
|
||||
{
|
||||
"background": "#112233",
|
||||
"unfocusedBackground": "#FF884400"
|
||||
},
|
||||
"window": null
|
||||
})" };
|
||||
|
||||
const auto schemeObject = VerifyParseSucceeded(emptyTheme);
|
||||
auto theme = Theme::FromJson(schemeObject);
|
||||
VERIFY_ARE_EQUAL(L"nullWindow", theme->Name());
|
||||
|
||||
VERIFY_IS_NOT_NULL(theme->TabRow());
|
||||
VERIFY_IS_NOT_NULL(theme->TabRow().Background());
|
||||
VERIFY_ARE_EQUAL(Settings::Model::ThemeColorType::Color, theme->TabRow().Background().ColorType());
|
||||
VERIFY_ARE_EQUAL(rgb(0x11, 0x22, 0x33), theme->TabRow().Background().Color());
|
||||
|
||||
VERIFY_IS_NULL(theme->Window());
|
||||
VERIFY_ARE_EQUAL(winrt::Windows::UI::Xaml::ElementTheme::Default, theme->RequestedTheme());
|
||||
}
|
||||
|
||||
void ThemeTests::ParseThemeWithNullThemeColor()
|
||||
{
|
||||
Log::Comment(L"These themes are all missing a tabRow background. Make sure we don't somehow default-construct one for them");
|
||||
|
||||
static constexpr std::string_view settingsString{ R"json({
|
||||
"themes": [
|
||||
{
|
||||
"name": "backgroundEmpty",
|
||||
"tabRow":
|
||||
{
|
||||
},
|
||||
"window":
|
||||
{
|
||||
"applicationTheme": "light",
|
||||
"useMica": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "backgroundNull",
|
||||
"tabRow":
|
||||
{
|
||||
"background": null
|
||||
},
|
||||
"window":
|
||||
{
|
||||
"applicationTheme": "light",
|
||||
"useMica": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "backgroundOmittedEntirely",
|
||||
"window":
|
||||
{
|
||||
"applicationTheme": "light",
|
||||
"useMica": true
|
||||
}
|
||||
}
|
||||
]
|
||||
})json" };
|
||||
|
||||
try
|
||||
{
|
||||
const auto settings{ winrt::make_self<CascadiaSettings>(settingsString, DefaultJson) };
|
||||
|
||||
const auto& themes{ settings->GlobalSettings().Themes() };
|
||||
{
|
||||
const auto& backgroundEmpty{ themes.Lookup(L"backgroundEmpty") };
|
||||
VERIFY_ARE_EQUAL(L"backgroundEmpty", backgroundEmpty.Name());
|
||||
VERIFY_IS_NOT_NULL(backgroundEmpty.TabRow());
|
||||
VERIFY_IS_NULL(backgroundEmpty.TabRow().Background());
|
||||
}
|
||||
{
|
||||
const auto& backgroundNull{ themes.Lookup(L"backgroundNull") };
|
||||
VERIFY_ARE_EQUAL(L"backgroundNull", backgroundNull.Name());
|
||||
VERIFY_IS_NOT_NULL(backgroundNull.TabRow());
|
||||
VERIFY_IS_NULL(backgroundNull.TabRow().Background());
|
||||
}
|
||||
{
|
||||
const auto& backgroundOmittedEntirely{ themes.Lookup(L"backgroundOmittedEntirely") };
|
||||
VERIFY_ARE_EQUAL(L"backgroundOmittedEntirely", backgroundOmittedEntirely.Name());
|
||||
VERIFY_IS_NULL(backgroundOmittedEntirely.TabRow());
|
||||
}
|
||||
}
|
||||
catch (const SettingsException& ex)
|
||||
{
|
||||
auto loadError = ex.Error();
|
||||
loadError;
|
||||
throw ex;
|
||||
}
|
||||
catch (const SettingsTypedDeserializationException& e)
|
||||
{
|
||||
auto deserializationErrorMessage = til::u8u16(e.what());
|
||||
Log::Comment(NoThrowString().Format(deserializationErrorMessage.c_str()));
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
void ThemeTests::InvalidCurrentTheme()
|
||||
{
|
||||
Log::Comment(L"Make sure specifying an invalid theme falls back to a sensible default.");
|
||||
|
||||
static constexpr std::string_view settingsString{ R"json({
|
||||
"theme": "foo",
|
||||
"themes": [
|
||||
{
|
||||
"name": "bar",
|
||||
"tabRow": {},
|
||||
"window":
|
||||
{
|
||||
"applicationTheme": "light",
|
||||
"useMica": true
|
||||
}
|
||||
}
|
||||
]
|
||||
})json" };
|
||||
|
||||
try
|
||||
{
|
||||
const auto settings{ winrt::make_self<CascadiaSettings>(settingsString, DefaultJson) };
|
||||
|
||||
VERIFY_ARE_EQUAL(1u, settings->Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(Settings::Model::SettingsLoadWarnings::UnknownTheme, settings->Warnings().GetAt(0));
|
||||
|
||||
const auto& themes{ settings->GlobalSettings().Themes() };
|
||||
{
|
||||
const auto& bar{ themes.Lookup(L"bar") };
|
||||
VERIFY_ARE_EQUAL(L"bar", bar.Name());
|
||||
VERIFY_IS_NOT_NULL(bar.TabRow());
|
||||
VERIFY_IS_NULL(bar.TabRow().Background());
|
||||
}
|
||||
|
||||
const auto currentTheme{ settings->GlobalSettings().CurrentTheme() };
|
||||
VERIFY_IS_NOT_NULL(currentTheme);
|
||||
VERIFY_ARE_EQUAL(L"system", currentTheme.Name());
|
||||
}
|
||||
catch (const SettingsException& ex)
|
||||
{
|
||||
auto loadError = ex.Error();
|
||||
loadError;
|
||||
throw ex;
|
||||
}
|
||||
catch (const SettingsTypedDeserializationException& e)
|
||||
{
|
||||
auto deserializationErrorMessage = til::u8u16(e.what());
|
||||
Log::Comment(NoThrowString().Format(deserializationErrorMessage.c_str()));
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1036,4 +1036,26 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
|
||||
return winrt::single_threaded_vector(std::move(vec));
|
||||
}
|
||||
|
||||
void Monarch::RequestMoveContent(winrt::hstring window,
|
||||
winrt::hstring content,
|
||||
uint32_t tabIndex)
|
||||
{
|
||||
auto windowId = _lookupPeasantIdForName(window);
|
||||
if (windowId == 0)
|
||||
{
|
||||
/* TODO! try the name as an integer ID */
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto targetPeasant{ _getPeasant(windowId) })
|
||||
{
|
||||
auto request = winrt::make_self<implementation::AttachRequest>(content, tabIndex);
|
||||
targetPeasant.AttachContentToWindow(*request);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*TODO! log */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo> GetPeasantInfos();
|
||||
Windows::Foundation::Collections::IVector<winrt::hstring> GetAllWindowLayouts();
|
||||
|
||||
void RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex);
|
||||
|
||||
TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs);
|
||||
TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
|
||||
@@ -60,6 +60,8 @@ namespace Microsoft.Terminal.Remoting
|
||||
Windows.Foundation.Collections.IVectorView<PeasantInfo> GetPeasantInfos { get; };
|
||||
Windows.Foundation.Collections.IVector<String> GetAllWindowLayouts();
|
||||
|
||||
void RequestMoveContent(String window, String content, UInt32 tabIndex);
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> ShowNotificationIconRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> HideNotificationIconRequested;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "GetWindowLayoutArgs.h"
|
||||
#include "Peasant.g.cpp"
|
||||
#include "../../types/inc/utils.hpp"
|
||||
#include "AttachRequest.g.cpp"
|
||||
|
||||
using namespace winrt;
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
@@ -275,6 +276,22 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
|
||||
void Peasant::AttachContentToWindow(Remoting::AttachRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
_AttachRequestedHandlers(*this, request);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
}
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Peasant_AttachContentToWindow",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
|
||||
void Peasant::Quit()
|
||||
{
|
||||
try
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "Peasant.g.h"
|
||||
#include "RenameRequestArgs.h"
|
||||
#include "AttachRequest.g.h"
|
||||
|
||||
namespace RemotingUnitTests
|
||||
{
|
||||
@@ -12,6 +13,18 @@ namespace RemotingUnitTests
|
||||
};
|
||||
namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
{
|
||||
struct AttachRequest : public AttachRequestT<AttachRequest>
|
||||
{
|
||||
WINRT_PROPERTY(winrt::hstring, Content);
|
||||
WINRT_PROPERTY(uint32_t, TabIndex);
|
||||
|
||||
public:
|
||||
AttachRequest(winrt::hstring content,
|
||||
uint32_t tabIndex) :
|
||||
_Content{ content },
|
||||
_TabIndex{ tabIndex } {};
|
||||
};
|
||||
|
||||
struct Peasant : public PeasantT<Peasant>
|
||||
{
|
||||
Peasant();
|
||||
@@ -32,6 +45,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
void RequestQuitAll();
|
||||
void Quit();
|
||||
|
||||
void AttachContentToWindow(Remoting::AttachRequest request);
|
||||
|
||||
winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs GetLastActivatedArgs();
|
||||
|
||||
winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs();
|
||||
@@ -47,12 +62,15 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
TYPED_EVENT(DisplayWindowIdRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(RenameRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::RenameRequestArgs);
|
||||
TYPED_EVENT(SummonRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior);
|
||||
|
||||
TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(QuitRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(GetWindowLayoutRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::GetWindowLayoutArgs);
|
||||
|
||||
TYPED_EVENT(AttachRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::AttachRequest);
|
||||
|
||||
private:
|
||||
Peasant(const uint64_t testPID);
|
||||
uint64_t _ourPID;
|
||||
|
||||
@@ -52,6 +52,11 @@ namespace Microsoft.Terminal.Remoting
|
||||
MonitorBehavior ToMonitor;
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass AttachRequest {
|
||||
String Content { get; };
|
||||
UInt32 TabIndex { get; };
|
||||
}
|
||||
|
||||
interface IPeasant
|
||||
{
|
||||
CommandlineArgs InitialArgs { get; };
|
||||
@@ -70,23 +75,31 @@ namespace Microsoft.Terminal.Remoting
|
||||
void RequestIdentifyWindows(); // Tells us to raise a IdentifyWindowsRequested
|
||||
void RequestRename(RenameRequestArgs args); // Tells us to raise a RenameRequested
|
||||
void Summon(SummonWindowBehavior behavior);
|
||||
|
||||
void RequestShowNotificationIcon();
|
||||
void RequestHideNotificationIcon();
|
||||
void RequestQuitAll();
|
||||
void Quit();
|
||||
String GetWindowLayout();
|
||||
|
||||
void AttachContentToWindow(AttachRequest request);
|
||||
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, WindowActivatedArgs> WindowActivated;
|
||||
event Windows.Foundation.TypedEventHandler<Object, CommandlineArgs> ExecuteCommandlineRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> IdentifyWindowsRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> DisplayWindowIdRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, RenameRequestArgs> RenameRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, SummonWindowBehavior> SummonRequested;
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> ShowNotificationIconRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> HideNotificationIconRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, GetWindowLayoutArgs> GetWindowLayoutRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> QuitAllRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> QuitRequested;
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, AttachRequest> AttachRequested;
|
||||
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass Peasant : IPeasant
|
||||
|
||||
@@ -788,4 +788,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
winrt::fire_and_forget WindowManager::RequestMoveContent(winrt::hstring window,
|
||||
winrt::hstring content,
|
||||
uint32_t tabIndex)
|
||||
{
|
||||
co_await winrt::resume_background();
|
||||
_monarch.RequestMoveContent(window, content, tabIndex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -48,9 +48,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
winrt::fire_and_forget RequestHideNotificationIcon();
|
||||
winrt::fire_and_forget RequestQuitAll();
|
||||
bool DoesQuakeWindowExist();
|
||||
|
||||
void UpdateActiveTabTitle(winrt::hstring title);
|
||||
Windows::Foundation::Collections::IVector<winrt::hstring> GetAllWindowLayouts();
|
||||
|
||||
winrt::fire_and_forget RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex);
|
||||
|
||||
TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs);
|
||||
TYPED_EVENT(BecameMonarch, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(WindowCreated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
|
||||
@@ -22,7 +22,11 @@ namespace Microsoft.Terminal.Remoting
|
||||
void RequestQuitAll();
|
||||
void UpdateActiveTabTitle(String title);
|
||||
Boolean DoesQuakeWindowExist();
|
||||
|
||||
Windows.Foundation.Collections.IVectorView<PeasantInfo> GetPeasantInfos();
|
||||
|
||||
void RequestMoveContent(String window, String content, UInt32 tabIndex);
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> BecameMonarch;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> WindowCreated;
|
||||
|
||||
@@ -175,7 +175,7 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
else if (const auto& realArgs = args.ActionArgs().try_as<MovePaneArgs>())
|
||||
{
|
||||
auto moved = _MovePane(realArgs.TabIndex());
|
||||
auto moved = _MovePane(realArgs);
|
||||
args.Handled(moved);
|
||||
}
|
||||
}
|
||||
@@ -201,10 +201,15 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
|
||||
_SplitPane(realArgs.SplitDirection(),
|
||||
// This is safe, we're already filtering so the value is (0, 1)
|
||||
::base::saturated_cast<float>(realArgs.SplitSize()),
|
||||
_MakePane(realArgs.TerminalArgs(), realArgs.SplitMode() == SplitType::Duplicate));
|
||||
// _SplitPaneActiveTab(realArgs.SplitDirection(),
|
||||
// // This is safe, we're already filtering so the value is (0, 1)
|
||||
// ::base::saturated_cast<float>(realArgs.SplitSize()),
|
||||
// _MakePane(realArgs.TerminalArgs(), realArgs.SplitMode() == SplitType::Duplicate));
|
||||
|
||||
_asyncSplitPaneActiveTab(realArgs.SplitDirection(),
|
||||
// This is safe, we're already filtering so the value is (0, 1)
|
||||
::base::saturated_cast<float>(realArgs.SplitSize()),
|
||||
_prepareContentProc(realArgs.TerminalArgs(), realArgs.SplitMode() == SplitType::Duplicate));
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
@@ -751,17 +756,8 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (const auto& realArgs = actionArgs.ActionArgs().try_as<MoveTabArgs>())
|
||||
{
|
||||
auto direction = realArgs.Direction();
|
||||
if (direction != MoveTabDirection::None)
|
||||
{
|
||||
if (auto focusedTabIndex = _GetFocusedTabIndex())
|
||||
{
|
||||
auto currentTabIndex = focusedTabIndex.value();
|
||||
auto delta = direction == MoveTabDirection::Forward ? 1 : -1;
|
||||
_TryMoveTab(currentTabIndex, currentTabIndex + delta);
|
||||
}
|
||||
}
|
||||
actionArgs.Handled(true);
|
||||
auto moved = _MoveTab(realArgs);
|
||||
actionArgs.Handled(moved);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1112,4 +1108,14 @@ namespace winrt::TerminalApp::implementation
|
||||
args.Handled(handled);
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleSwitchSelectionEndpoint(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
if (const auto& control{ _GetActiveControl() })
|
||||
{
|
||||
const auto handled = control.SwitchSelectionEndpoint();
|
||||
args.Handled(handled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ static const std::array settingsLoadWarningsLabels {
|
||||
USES_RESOURCE(L"InvalidSplitSize"),
|
||||
USES_RESOURCE(L"FailedToParseStartupActions"),
|
||||
USES_RESOURCE(L"FailedToParseSubCommands"),
|
||||
USES_RESOURCE(L"UnknownTheme"),
|
||||
};
|
||||
static const std::array settingsLoadErrorsLabels {
|
||||
USES_RESOURCE(L"NoProfilesText"),
|
||||
@@ -292,7 +293,7 @@ namespace winrt::TerminalApp::implementation
|
||||
_root->Maximized(true);
|
||||
}
|
||||
|
||||
if (WI_IsFlagSet(launchMode, LaunchMode::FullscreenMode))
|
||||
if (WI_IsFlagSet(launchMode, LaunchMode::FullscreenMode) && !IsQuakeWindow())
|
||||
{
|
||||
_root->SetFullscreen(true);
|
||||
}
|
||||
@@ -367,11 +368,12 @@ namespace winrt::TerminalApp::implementation
|
||||
// details here, but it does have the desired effect.
|
||||
// It's not enough to set the theme on the dialog alone.
|
||||
auto themingLambda{ [this](const Windows::Foundation::IInspectable& sender, const RoutedEventArgs&) {
|
||||
auto theme{ _settings.GlobalSettings().Theme() };
|
||||
auto theme{ _settings.GlobalSettings().CurrentTheme() };
|
||||
auto requestedTheme{ theme.RequestedTheme() };
|
||||
auto element{ sender.try_as<winrt::Windows::UI::Xaml::FrameworkElement>() };
|
||||
while (element)
|
||||
{
|
||||
element.RequestedTheme(theme);
|
||||
element.RequestedTheme(requestedTheme);
|
||||
element = element.Parent().try_as<winrt::Windows::UI::Xaml::FrameworkElement>();
|
||||
}
|
||||
} };
|
||||
@@ -737,13 +739,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
winrt::Windows::UI::Xaml::ElementTheme AppLogic::GetRequestedTheme()
|
||||
{
|
||||
if (!_loadedInitialSettings)
|
||||
{
|
||||
// Load settings if we haven't already
|
||||
LoadSettings();
|
||||
}
|
||||
|
||||
return _settings.GlobalSettings().Theme();
|
||||
return Theme().RequestedTheme();
|
||||
}
|
||||
|
||||
bool AppLogic::GetShowTabsInTitlebar()
|
||||
@@ -962,9 +958,16 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
CATCH_LOG()
|
||||
|
||||
// Method Description:
|
||||
// - Update the current theme of the application. This will trigger our
|
||||
// RequestedThemeChanged event, to have our host change the theme of the
|
||||
// root of the application.
|
||||
// Arguments:
|
||||
// - newTheme: The ElementTheme to apply to our elements.
|
||||
void AppLogic::_RefreshThemeRoutine()
|
||||
{
|
||||
_ApplyTheme(_settings.GlobalSettings().Theme());
|
||||
// Propagate the event to the host layer, so it can update its own UI
|
||||
_RequestedThemeChangedHandlers(*this, Theme());
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
@@ -1073,18 +1076,6 @@ namespace winrt::TerminalApp::implementation
|
||||
return _settings;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Update the current theme of the application. This will trigger our
|
||||
// RequestedThemeChanged event, to have our host change the theme of the
|
||||
// root of the application.
|
||||
// Arguments:
|
||||
// - newTheme: The ElementTheme to apply to our elements.
|
||||
void AppLogic::_ApplyTheme(const Windows::UI::Xaml::ElementTheme& newTheme)
|
||||
{
|
||||
// Propagate the event to the host layer, so it can update its own UI
|
||||
_RequestedThemeChangedHandlers(*this, newTheme);
|
||||
}
|
||||
|
||||
UIElement AppLogic::GetRoot() noexcept
|
||||
{
|
||||
return _root.as<winrt::Windows::UI::Xaml::Controls::Control>();
|
||||
@@ -1219,6 +1210,19 @@ namespace winrt::TerminalApp::implementation
|
||||
return {};
|
||||
}
|
||||
|
||||
winrt::Windows::UI::Xaml::Media::Brush AppLogic::TitlebarBrush()
|
||||
{
|
||||
if (_root)
|
||||
{
|
||||
return _root->TitlebarBrush();
|
||||
}
|
||||
return { nullptr };
|
||||
}
|
||||
void AppLogic::WindowActivated(const bool activated)
|
||||
{
|
||||
_root->WindowActivated(activated);
|
||||
}
|
||||
|
||||
bool AppLogic::HasCommandlineArguments() const noexcept
|
||||
{
|
||||
return _hasCommandLineArguments;
|
||||
@@ -1645,4 +1649,22 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
return _settings.GlobalSettings().ShowTitleInTitlebar();
|
||||
}
|
||||
|
||||
Microsoft::Terminal::Settings::Model::Theme AppLogic::Theme()
|
||||
{
|
||||
if (!_loadedInitialSettings)
|
||||
{
|
||||
// Load settings if we haven't already
|
||||
LoadSettings();
|
||||
}
|
||||
return _settings.GlobalSettings().CurrentTheme();
|
||||
}
|
||||
|
||||
void AppLogic::AttachContent(winrt::hstring content, uint32_t tabIndex)
|
||||
{
|
||||
if (_root)
|
||||
{
|
||||
_root->AttachContent(content, tabIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,18 +117,33 @@ namespace winrt::TerminalApp::implementation
|
||||
void WindowVisibilityChanged(const bool showOrHide);
|
||||
|
||||
winrt::TerminalApp::TaskbarState TaskbarState();
|
||||
winrt::Windows::UI::Xaml::Media::Brush TitlebarBrush();
|
||||
void WindowActivated(const bool activated);
|
||||
|
||||
bool GetMinimizeToNotificationArea();
|
||||
bool GetAlwaysShowNotificationIcon();
|
||||
bool GetShowTitleInTitlebar();
|
||||
|
||||
void AttachContent(winrt::hstring content, uint32_t tabIndex);
|
||||
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog);
|
||||
void DismissDialog();
|
||||
|
||||
Windows::Foundation::Collections::IMapView<Microsoft::Terminal::Control::KeyChord, Microsoft::Terminal::Settings::Model::Command> GlobalHotkeys();
|
||||
|
||||
Microsoft::Terminal::Settings::Model::Theme Theme();
|
||||
|
||||
// -------------------------------- WinRT Events ---------------------------------
|
||||
TYPED_EVENT(RequestedThemeChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::ElementTheme);
|
||||
// PropertyChanged is surprisingly not a typed event, so we'll define that one manually.
|
||||
// Usually we'd just do
|
||||
// WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
//
|
||||
// But what we're doing here is exposing the Page's PropertyChanged _as
|
||||
// our own event_. It's a FORWARDED_CALLBACK, essentially.
|
||||
winrt::event_token PropertyChanged(Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler) { return _root->PropertyChanged(handler); }
|
||||
void PropertyChanged(winrt::event_token const& token) { _root->PropertyChanged(token); }
|
||||
|
||||
TYPED_EVENT(RequestedThemeChanged, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Settings::Model::Theme);
|
||||
TYPED_EVENT(SettingsChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(SystemMenuChangeRequested, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SystemMenuChangeArgs);
|
||||
|
||||
@@ -183,8 +198,6 @@ namespace winrt::TerminalApp::implementation
|
||||
void _ReloadSettings();
|
||||
void _OpenSettingsUI();
|
||||
|
||||
void _ApplyTheme(const Windows::UI::Xaml::ElementTheme& newTheme);
|
||||
|
||||
bool _hasCommandLineArguments{ false };
|
||||
bool _hasSettingsStartupActions{ false };
|
||||
std::vector<Microsoft::Terminal::Settings::Model::SettingsLoadWarnings> _warnings;
|
||||
@@ -205,11 +218,14 @@ namespace winrt::TerminalApp::implementation
|
||||
FORWARDED_TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs, _root, RenameWindowRequested);
|
||||
FORWARDED_TYPED_EVENT(IsQuakeWindowChanged, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, IsQuakeWindowChanged);
|
||||
FORWARDED_TYPED_EVENT(SummonWindowRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, SummonWindowRequested);
|
||||
|
||||
FORWARDED_TYPED_EVENT(CloseRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, CloseRequested);
|
||||
FORWARDED_TYPED_EVENT(OpenSystemMenu, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, OpenSystemMenu);
|
||||
FORWARDED_TYPED_EVENT(QuitRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, QuitRequested);
|
||||
FORWARDED_TYPED_EVENT(ShowWindowChanged, Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Control::ShowWindowArgs, _root, ShowWindowChanged);
|
||||
|
||||
FORWARDED_TYPED_EVENT(RequestMoveContent, Windows::Foundation::IInspectable, winrt::TerminalApp::RequestMoveContentArgs, _root, RequestMoveContent);
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class TerminalAppLocalTests::CommandlineTest;
|
||||
#endif
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace TerminalApp
|
||||
|
||||
// See IDialogPresenter and TerminalPage's DialogPresenter for more
|
||||
// information.
|
||||
[default_interface] runtimeclass AppLogic : IDirectKeyListener, IDialogPresenter
|
||||
[default_interface] runtimeclass AppLogic : IDirectKeyListener, IDialogPresenter, Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
AppLogic();
|
||||
|
||||
@@ -94,6 +94,8 @@ namespace TerminalApp
|
||||
void WindowVisibilityChanged(Boolean showOrHide);
|
||||
|
||||
TaskbarState TaskbarState{ get; };
|
||||
Windows.UI.Xaml.Media.Brush TitlebarBrush { get; };
|
||||
void WindowActivated(Boolean activated);
|
||||
|
||||
Boolean ShouldUsePersistedLayout();
|
||||
Boolean ShouldImmediatelyHandoffToElevated();
|
||||
@@ -105,7 +107,10 @@ namespace TerminalApp
|
||||
Boolean GetAlwaysShowNotificationIcon();
|
||||
Boolean GetShowTitleInTitlebar();
|
||||
|
||||
Microsoft.Terminal.Settings.Model.Theme Theme { get; };
|
||||
|
||||
FindTargetWindowResult FindTargetWindow(String[] args);
|
||||
void AttachContent(String content, UInt32 tabIndex);
|
||||
|
||||
Windows.Foundation.Collections.IMapView<Microsoft.Terminal.Control.KeyChord, Microsoft.Terminal.Settings.Model.Command> GlobalHotkeys();
|
||||
|
||||
@@ -117,7 +122,7 @@ namespace TerminalApp
|
||||
event Windows.Foundation.TypedEventHandler<Object, Windows.UI.Xaml.UIElement> SetTitleBarContent;
|
||||
event Windows.Foundation.TypedEventHandler<Object, String> TitleChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, LastTabClosedEventArgs> LastTabClosed;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Windows.UI.Xaml.ElementTheme> RequestedThemeChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Settings.Model.Theme> RequestedThemeChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> FocusModeChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> FullscreenChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> ChangeMaximizeRequested;
|
||||
@@ -134,5 +139,8 @@ namespace TerminalApp
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> QuitRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, TerminalApp.SystemMenuChangeArgs> SystemMenuChangeRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Control.ShowWindowArgs> ShowWindowChanged;
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, RequestMoveContentArgs> RequestMoveContent;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +111,15 @@ namespace winrt::Microsoft::TerminalApp::implementation
|
||||
|
||||
void DebugTapConnection::_OutputHandler(const hstring str)
|
||||
{
|
||||
_TerminalOutputHandlers(til::visualize_control_codes(str));
|
||||
auto output = til::visualize_control_codes(str);
|
||||
// To make the output easier to read, we introduce a line break whenever
|
||||
// an LF control is encountered. But at this point, the LF would have
|
||||
// been converted to U+240A (␊), so that's what we need to search for.
|
||||
for (size_t lfPos = 0; (lfPos = output.find(L'\u240A', lfPos)) != std::wstring::npos;)
|
||||
{
|
||||
output.insert(++lfPos, L"\r\n");
|
||||
}
|
||||
_TerminalOutputHandlers(output);
|
||||
}
|
||||
|
||||
// Called by the DebugInputTapConnection to print user input
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "winrt/Microsoft.UI.Xaml.Controls.h"
|
||||
|
||||
#include "HighlightedTextSegment.g.h"
|
||||
#include "HighlightedText.g.h"
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ Pane::Pane(std::shared_ptr<Pane> first,
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - Arguments appropriate for a SplitPane or NewTab action
|
||||
NewTerminalArgs Pane::GetTerminalArgsForPane() const
|
||||
NewTerminalArgs Pane::GetTerminalArgsForPane(const bool asContent) const
|
||||
{
|
||||
// Leaves are the only things that have controls
|
||||
assert(_IsLeaf());
|
||||
@@ -164,6 +164,11 @@ NewTerminalArgs Pane::GetTerminalArgsForPane() const
|
||||
// object. That would work for schemes set by the Terminal, but not ones set
|
||||
// by VT, but that seems good enough.
|
||||
|
||||
if (asContent)
|
||||
{
|
||||
args.ContentGuid(_control.ContentGuid());
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
@@ -175,14 +180,15 @@ NewTerminalArgs Pane::GetTerminalArgsForPane() const
|
||||
// Arguments:
|
||||
// - currentId: the id to use for the current/first pane
|
||||
// - nextId: the id to use for a new pane if we split
|
||||
// - asContent: TODO!
|
||||
// Return Value:
|
||||
// - The state from building the startup actions, includes a vector of commands,
|
||||
// the original root pane, the id of the focused pane, and the number of panes
|
||||
// created.
|
||||
Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, uint32_t nextId)
|
||||
Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, uint32_t nextId, const bool asContent)
|
||||
{
|
||||
// if we are a leaf then all there is to do is defer to the parent.
|
||||
if (_IsLeaf())
|
||||
if (/*!asContent && */ _IsLeaf())
|
||||
{
|
||||
if (_lastActive)
|
||||
{
|
||||
@@ -195,16 +201,29 @@ Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, uint32_t n
|
||||
auto buildSplitPane = [&](auto newPane) {
|
||||
ActionAndArgs actionAndArgs;
|
||||
actionAndArgs.Action(ShortcutAction::SplitPane);
|
||||
const auto terminalArgs{ newPane->GetTerminalArgsForPane() };
|
||||
const auto terminalArgs{ newPane->GetTerminalArgsForPane(asContent) };
|
||||
// When creating a pane the split size is the size of the new pane
|
||||
// and not position.
|
||||
const auto splitDirection = _splitState == SplitState::Horizontal ? SplitDirection::Down : SplitDirection::Right;
|
||||
SplitPaneArgs args{ SplitType::Manual, splitDirection, 1. - _desiredSplitPosition, terminalArgs };
|
||||
// const auto splitSize = (asContent && _IsLeaf()) ? .5 : (1. - _desiredSplitPosition);
|
||||
const auto splitSize = (1. - _desiredSplitPosition);
|
||||
SplitPaneArgs args{ SplitType::Manual, splitDirection, splitSize, terminalArgs };
|
||||
actionAndArgs.Args(args);
|
||||
|
||||
return actionAndArgs;
|
||||
};
|
||||
|
||||
// if (asContent && _IsLeaf())
|
||||
// {
|
||||
// // TODO! This probably won't work. We probably do need to ask the parent
|
||||
// // of this pane to generate the action for us. Consider moving a pane
|
||||
// // that's 25% of the parent - the pane doesn't know that. Only the
|
||||
// // parent does. When we recieve it, we can determine if we're putting it
|
||||
// // into a tab or a pane, and then parse the NewTerminalArgs out of
|
||||
// // either the splitPane or the newTab action.
|
||||
// return { { buildSplitPane(shared_from_this()) }, shared_from_this(), currentId, 1 };
|
||||
// }
|
||||
|
||||
auto buildMoveFocus = [](auto direction) {
|
||||
MoveFocusArgs args{ direction };
|
||||
|
||||
|
||||
@@ -91,8 +91,8 @@ public:
|
||||
std::optional<uint32_t> focusedPaneId;
|
||||
uint32_t panesCreated;
|
||||
};
|
||||
BuildStartupState BuildStartupActions(uint32_t currentId, uint32_t nextId);
|
||||
winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetTerminalArgsForPane() const;
|
||||
BuildStartupState BuildStartupActions(uint32_t currentId, uint32_t nextId, const bool asContent = false);
|
||||
winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetTerminalArgsForPane(const bool asContent = false) const;
|
||||
|
||||
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings,
|
||||
const winrt::Microsoft::Terminal::Settings::Model::Profile& profile);
|
||||
|
||||
@@ -242,6 +242,10 @@
|
||||
<value>• Found a keybinding that was missing a required parameter value. This keybinding will be ignored.</value>
|
||||
<comment>{Locked="•"} This glyph is a bullet, used in a bulleted list.</comment>
|
||||
</data>
|
||||
<data name="UnknownTheme" xml:space="preserve">
|
||||
<value>• The specified "theme" was not found in the list of themes. Temporarily falling back to the default value.</value>
|
||||
<comment>{Locked="•"} This glyph is a bullet, used in a bulleted list.</comment>
|
||||
</data>
|
||||
<data name="LegacyGlobalsProperty" xml:space="preserve">
|
||||
<value>The "globals" property is deprecated - your settings might need updating. </value>
|
||||
<comment>{Locked="\"globals\""} </comment>
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - The list of actions.
|
||||
std::vector<ActionAndArgs> SettingsTab::BuildStartupActions() const
|
||||
std::vector<ActionAndArgs> SettingsTab::BuildStartupActions(const bool /*asContent*/) const
|
||||
{
|
||||
ActionAndArgs action;
|
||||
action.Action(ShortcutAction::OpenSettings);
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace winrt::TerminalApp::implementation
|
||||
void UpdateSettings(Microsoft::Terminal::Settings::Model::CascadiaSettings settings);
|
||||
void Focus(winrt::Windows::UI::Xaml::FocusState focusState) override;
|
||||
|
||||
std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions() const override;
|
||||
std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions(const bool asContent = false) const override;
|
||||
|
||||
private:
|
||||
void _MakeTabViewItem() override;
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
void UpdateTabViewIndex(const uint32_t idx, const uint32_t numTabs);
|
||||
void SetActionMap(const Microsoft::Terminal::Settings::Model::IActionMapView& actionMap);
|
||||
virtual std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions() const = 0;
|
||||
virtual std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions(const bool asContent = false) const = 0;
|
||||
|
||||
WINRT_CALLBACK(RequestFocusActiveControl, winrt::delegate<void()>);
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - existingConnection: An optional connection that is already established to a PTY
|
||||
// for this tab to host instead of creating one.
|
||||
// If not defined, the tab will create the connection.
|
||||
HRESULT TerminalPage::_OpenNewTab(const NewTerminalArgs& newTerminalArgs, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection)
|
||||
HRESULT TerminalPage::_OpenNewTab(const NewTerminalArgs& newTerminalArgs)
|
||||
try
|
||||
{
|
||||
const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) };
|
||||
@@ -72,10 +72,10 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
return S_FALSE;
|
||||
}
|
||||
const auto settings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings) };
|
||||
const auto controlSettings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings) };
|
||||
|
||||
// Try to handle auto-elevation
|
||||
if (_maybeElevate(newTerminalArgs, settings, profile))
|
||||
if (_maybeElevate(newTerminalArgs, controlSettings, profile))
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
@@ -83,35 +83,42 @@ namespace winrt::TerminalApp::implementation
|
||||
// unfortunately. This seems to be due to Centennial quirks. It works
|
||||
// unpackaged, but not packaged.
|
||||
//
|
||||
// This call to _MakePane won't return nullptr, we already checked that
|
||||
// case above with the _maybeElevate call.
|
||||
_CreateNewTabFromPane(_MakePane(newTerminalArgs, false, existingConnection));
|
||||
// // This call to _MakePane won't return nullptr, we already checked that
|
||||
// // case above with the _maybeElevate call.
|
||||
// _CreateNewTabFromPane(_MakePane(newTerminalArgs, false, nullptr));
|
||||
|
||||
const auto tabCount = _tabs.Size();
|
||||
const auto usedManualProfile = (newTerminalArgs != nullptr) &&
|
||||
(newTerminalArgs.ProfileIndex() != nullptr ||
|
||||
newTerminalArgs.Profile().empty());
|
||||
// TerminalSettingsCreateResult controlSettings{ nullptr };
|
||||
// Profile profile{ nullptr };
|
||||
// _evaluateSettings(newTerminalArgs, false /*duplicate*/, controlSettings, profile);
|
||||
auto initContentProc = (newTerminalArgs && newTerminalArgs.ContentGuid() != winrt::guid{}) ?
|
||||
_AttachToContentProcess(newTerminalArgs.ContentGuid()) :
|
||||
_CreateNewContentProcess(profile, controlSettings);
|
||||
_createNewTabFromContent(PreparedContent{ initContentProc, controlSettings, profile });
|
||||
// const auto tabCount = _tabs.Size();
|
||||
// const auto usedManualProfile = (newTerminalArgs != nullptr) &&
|
||||
// (newTerminalArgs.ProfileIndex() != nullptr ||
|
||||
// newTerminalArgs.Profile().empty());
|
||||
|
||||
// Lookup the name of the color scheme used by this profile.
|
||||
const auto scheme = _settings.GetColorSchemeForProfile(profile);
|
||||
// If they explicitly specified `null` as the scheme (indicating _no_ scheme), log
|
||||
// that as the empty string.
|
||||
const auto schemeName = scheme ? scheme.Name() : L"\0";
|
||||
// // Lookup the name of the color scheme used by this profile.
|
||||
// const auto scheme = _settings.GetColorSchemeForProfile(profile);
|
||||
// // If they explicitly specified `null` as the scheme (indicating _no_ scheme), log
|
||||
// // that as the empty string.
|
||||
// const auto schemeName = scheme ? scheme.Name() : L"\0";
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider
|
||||
"TabInformation",
|
||||
TraceLoggingDescription("Event emitted upon new tab creation in TerminalApp"),
|
||||
TraceLoggingUInt32(1u, "EventVer", "Version of this event"),
|
||||
TraceLoggingUInt32(tabCount, "TabCount", "Count of tabs currently opened in TerminalApp"),
|
||||
TraceLoggingBool(usedManualProfile, "ProfileSpecified", "Whether the new tab specified a profile explicitly"),
|
||||
TraceLoggingGuid(profile.Guid(), "ProfileGuid", "The GUID of the profile spawned in the new tab"),
|
||||
TraceLoggingBool(settings.DefaultSettings().UseAcrylic(), "UseAcrylic", "The acrylic preference from the settings"),
|
||||
TraceLoggingFloat64(settings.DefaultSettings().Opacity(), "TintOpacity", "Opacity preference from the settings"),
|
||||
TraceLoggingWideString(settings.DefaultSettings().FontFace().c_str(), "FontFace", "Font face chosen in the settings"),
|
||||
TraceLoggingWideString(schemeName.data(), "SchemeName", "Color scheme set in the settings"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
|
||||
// TraceLoggingWrite(
|
||||
// g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider
|
||||
// "TabInformation",
|
||||
// TraceLoggingDescription("Event emitted upon new tab creation in TerminalApp"),
|
||||
// TraceLoggingUInt32(1u, "EventVer", "Version of this event"),
|
||||
// TraceLoggingUInt32(tabCount, "TabCount", "Count of tabs currently opened in TerminalApp"),
|
||||
// TraceLoggingBool(usedManualProfile, "ProfileSpecified", "Whether the new tab specified a profile explicitly"),
|
||||
// TraceLoggingGuid(profile.Guid(), "ProfileGuid", "The GUID of the profile spawned in the new tab"),
|
||||
// TraceLoggingBool(settings.DefaultSettings().UseAcrylic(), "UseAcrylic", "The acrylic preference from the settings"),
|
||||
// TraceLoggingFloat64(settings.DefaultSettings().Opacity(), "TintOpacity", "Opacity preference from the settings"),
|
||||
// TraceLoggingWideString(settings.DefaultSettings().FontFace().c_str(), "FontFace", "Font face chosen in the settings"),
|
||||
// TraceLoggingWideString(schemeName.data(), "SchemeName", "Color scheme set in the settings"),
|
||||
// TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
// TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@@ -123,10 +130,20 @@ namespace winrt::TerminalApp::implementation
|
||||
// - newTabImpl: the uninitialized tab.
|
||||
void TerminalPage::_InitializeTab(winrt::com_ptr<TerminalTab> newTabImpl)
|
||||
{
|
||||
newTabImpl->Initialize();
|
||||
// newTabImpl->Initialize();
|
||||
|
||||
uint32_t insertPosition = _tabs.Size();
|
||||
if (_settings.GlobalSettings().NewTabPosition() == NewTabPosition::AfterCurrentTab)
|
||||
{
|
||||
auto currentTabIndex = _GetFocusedTabIndex();
|
||||
if (currentTabIndex.has_value())
|
||||
{
|
||||
insertPosition = currentTabIndex.value() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the new tab to the list of our tabs.
|
||||
_tabs.Append(*newTabImpl);
|
||||
_tabs.InsertAt(insertPosition, *newTabImpl);
|
||||
_mruTabs.Append(*newTabImpl);
|
||||
|
||||
newTabImpl->SetDispatch(*_actionDispatch);
|
||||
@@ -154,6 +171,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// Possibly update the icon of the tab.
|
||||
page->_UpdateTabIcon(*tab);
|
||||
|
||||
page->_updateThemeColors();
|
||||
|
||||
// Update the taskbar progress as well. We'll raise our own
|
||||
// SetTaskbarProgress event here, to get tell the hosting
|
||||
// application to re-query this value from us.
|
||||
@@ -193,7 +212,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
if (page && tab)
|
||||
{
|
||||
page->_SplitTab(*tab);
|
||||
page->_SplitTab(tab);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -220,16 +239,20 @@ namespace winrt::TerminalApp::implementation
|
||||
});
|
||||
|
||||
auto tabViewItem = newTabImpl->TabViewItem();
|
||||
_tabView.TabItems().Append(tabViewItem);
|
||||
_tabView.TabItems().InsertAt(insertPosition, tabViewItem);
|
||||
|
||||
// Set this tab's icon to the icon from the user's profile
|
||||
if (const auto profile{ newTabImpl->GetFocusedProfile() })
|
||||
{
|
||||
if (!profile.Icon().empty())
|
||||
{
|
||||
newTabImpl->UpdateIcon(profile.Icon());
|
||||
}
|
||||
}
|
||||
//
|
||||
// TODO! This doesn't need ot live in TerminalPage like, at all. This
|
||||
// should get moved inside TerminalTab::AttachRootPane, or
|
||||
// TerminalTab::Initialize or something.
|
||||
// if (const auto profile{ newTabImpl->GetFocusedProfile() })
|
||||
// {
|
||||
// if (!profile.Icon().empty())
|
||||
// {
|
||||
// newTabImpl->UpdateIcon(profile.Icon());
|
||||
// }
|
||||
// }
|
||||
|
||||
tabViewItem.PointerReleased({ this, &TerminalPage::_OnTabClick });
|
||||
|
||||
@@ -279,6 +302,41 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
|
||||
winrt::fire_and_forget TerminalPage::_createNewTabFromContent(PreparedContent preppedContent,
|
||||
std::function<void(const winrt::com_ptr<TerminalTab>&)> postInitTab /* defaults to nullptr*/)
|
||||
{
|
||||
auto newTabImpl = winrt::make_self<TerminalTab>(nullptr);
|
||||
// TODO! This tab should have a Content that's initialized with a blank
|
||||
// grid that takes up the whole space, with the BG set to the BG color
|
||||
// of the TerminalControl. So that the tab has something to show
|
||||
// initially.
|
||||
//
|
||||
// Alternatively, the Control could be initialized with a
|
||||
// AsyncAction<ContentProcess> and then when _that_ returns, the
|
||||
// TermControl starts to initialize itself?
|
||||
//
|
||||
// That's an idea. We do already have the settings for the control, just not the content. Huh.
|
||||
_InitializeTab(newTabImpl); // Adds tab to list, tabview
|
||||
|
||||
// If the caller requested additional setup for the tab, do that now.
|
||||
// For example, DuplicateTab uses this to copy the runtime tab text from
|
||||
// the old tab to the new one.
|
||||
if (postInitTab)
|
||||
{
|
||||
postInitTab(newTabImpl);
|
||||
}
|
||||
|
||||
// TODO! Do we need both this and the resume_background in _CreateNewContentProcess
|
||||
co_await winrt::resume_background();
|
||||
auto content = co_await preppedContent.initContentProc;
|
||||
co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::High);
|
||||
|
||||
auto pane = _makePaneFromContent(content, preppedContent.controlSettings, preppedContent.profile);
|
||||
newTabImpl->AttachRootPane(pane);
|
||||
|
||||
// TODO! DebugTap
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Get the icon of the currently focused terminal control, and set its
|
||||
// tab's icon to that icon.
|
||||
@@ -348,30 +406,40 @@ namespace winrt::TerminalApp::implementation
|
||||
// In the future, it may be preferable to just duplicate the
|
||||
// current control's live settings (which will include changes
|
||||
// made through VT).
|
||||
_CreateNewTabFromPane(_MakePane(nullptr, true, nullptr));
|
||||
|
||||
const auto runtimeTabText{ tab.GetTabText() };
|
||||
if (!runtimeTabText.empty())
|
||||
{
|
||||
if (auto newTab{ _GetFocusedTabImpl() })
|
||||
// _CreateNewTabFromPane(_MakePane(nullptr, true, nullptr));
|
||||
|
||||
auto initRuntimeTabTitle = [&tab](auto& newTab) {
|
||||
const auto runtimeTabText{ tab.GetTabText() };
|
||||
if (!runtimeTabText.empty())
|
||||
{
|
||||
newTab->SetTabText(runtimeTabText);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// TerminalSettingsCreateResult controlSettings{ nullptr };
|
||||
// Profile profile{ nullptr };
|
||||
// _evaluateSettings(nullptr, true, controlSettings, profile);
|
||||
// auto initContentProc = _CreateNewContentProcess(profile, controlSettings);
|
||||
auto preppedContent = _prepareContentProc(nullptr, true);
|
||||
_createNewTabFromContent(preppedContent, initRuntimeTabTitle);
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Sets the specified tab as the focused tab and splits its active pane
|
||||
// - Sets the specified tab as the focused tab and splits its active pane.
|
||||
// This will duplicate the profile that's currently active in the given
|
||||
// tab.
|
||||
// Arguments:
|
||||
// - tab: tab to split
|
||||
void TerminalPage::_SplitTab(TerminalTab& tab)
|
||||
void TerminalPage::_SplitTab(winrt::com_ptr<TerminalTab>& tab)
|
||||
{
|
||||
try
|
||||
{
|
||||
_SetFocusedTab(tab);
|
||||
_SplitPane(tab, SplitDirection::Automatic, 0.5f, _MakePane(nullptr, true));
|
||||
_SetFocusedTab(*tab);
|
||||
// _SplitPaneOnTab(tab, SplitDirection::Automatic, 0.5f, _MakePane(nullptr, true));
|
||||
_asyncSplitPaneOnTab(tab, SplitDirection::Automatic, 0.5f, _prepareContentProc(nullptr, true));
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
@@ -925,6 +993,8 @@ namespace winrt::TerminalApp::implementation
|
||||
_TitleChangedHandlers(*this, tab.Title());
|
||||
}
|
||||
|
||||
_updateThemeColors();
|
||||
|
||||
auto tab_impl = _GetTerminalTabImpl(tab);
|
||||
if (tab_impl)
|
||||
{
|
||||
|
||||
@@ -60,7 +60,8 @@
|
||||
FontSize="12">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip Placement="Mouse">
|
||||
<TextBlock IsTextSelectionEnabled="False">
|
||||
<TextBlock IsTextSelectionEnabled="False"
|
||||
TextWrapping="Wrap">
|
||||
<Run x:Uid="NewTabRun" /> <LineBreak />
|
||||
<Run x:Uid="NewPaneRun"
|
||||
FontStyle="Italic" /> <LineBreak />
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,7 @@
|
||||
#include "AppKeyBindings.h"
|
||||
#include "AppCommandlineArgs.h"
|
||||
#include "RenameWindowRequestedArgs.g.h"
|
||||
#include "RequestMoveContentArgs.g.h"
|
||||
#include "Toast.h"
|
||||
|
||||
#define DECLARE_ACTION_HANDLER(action) void _Handle##action(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
|
||||
@@ -50,6 +51,26 @@ namespace winrt::TerminalApp::implementation
|
||||
_ProposedName{ name } {};
|
||||
};
|
||||
|
||||
struct RequestMoveContentArgs : RequestMoveContentArgsT<RequestMoveContentArgs>
|
||||
{
|
||||
WINRT_PROPERTY(winrt::hstring, Window);
|
||||
WINRT_PROPERTY(winrt::hstring, Content);
|
||||
WINRT_PROPERTY(uint32_t, TabIndex);
|
||||
|
||||
public:
|
||||
RequestMoveContentArgs(const winrt::hstring window, const winrt::hstring content, uint32_t tabIndex) :
|
||||
_Window{ window },
|
||||
_Content{ content },
|
||||
_TabIndex{ tabIndex } {};
|
||||
};
|
||||
|
||||
struct PreparedContent
|
||||
{
|
||||
Windows::Foundation::IAsyncOperation<winrt::Microsoft::Terminal::Control::ContentProcess> initContentProc{ nullptr };
|
||||
winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult controlSettings{ nullptr };
|
||||
winrt::Microsoft::Terminal::Settings::Model::Profile profile{ nullptr };
|
||||
};
|
||||
|
||||
struct TerminalPage : TerminalPageT<TerminalPage>
|
||||
{
|
||||
public:
|
||||
@@ -131,9 +152,13 @@ namespace winrt::TerminalApp::implementation
|
||||
winrt::hstring WindowIdForDisplay() const noexcept;
|
||||
winrt::hstring WindowNameForDisplay() const noexcept;
|
||||
bool IsQuakeWindow() const noexcept;
|
||||
|
||||
bool IsElevated() const noexcept;
|
||||
|
||||
void OpenSettingsUI();
|
||||
void WindowActivated(const bool activated);
|
||||
|
||||
winrt::fire_and_forget AttachContent(winrt::hstring content, uint32_t tabIndex);
|
||||
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
|
||||
@@ -152,11 +177,16 @@ namespace winrt::TerminalApp::implementation
|
||||
TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs);
|
||||
TYPED_EVENT(IsQuakeWindowChanged, IInspectable, IInspectable);
|
||||
TYPED_EVENT(SummonWindowRequested, IInspectable, IInspectable);
|
||||
|
||||
TYPED_EVENT(CloseRequested, IInspectable, IInspectable);
|
||||
TYPED_EVENT(OpenSystemMenu, IInspectable, IInspectable);
|
||||
TYPED_EVENT(QuitRequested, IInspectable, IInspectable);
|
||||
TYPED_EVENT(ShowWindowChanged, IInspectable, winrt::Microsoft::Terminal::Control::ShowWindowArgs)
|
||||
|
||||
TYPED_EVENT(RequestMoveContent, Windows::Foundation::IInspectable, winrt::TerminalApp::RequestMoveContentArgs);
|
||||
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, TitlebarBrush, _PropertyChangedHandlers, nullptr);
|
||||
|
||||
private:
|
||||
friend struct TerminalPageT<TerminalPage>; // for Xaml to bind events
|
||||
std::optional<HWND> _hostingHwnd;
|
||||
@@ -196,6 +226,8 @@ namespace winrt::TerminalApp::implementation
|
||||
std::optional<int> _rearrangeFrom{};
|
||||
std::optional<int> _rearrangeTo{};
|
||||
bool _removing{ false };
|
||||
|
||||
bool _activated{ false };
|
||||
bool _visible{ true };
|
||||
|
||||
std::vector<std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs>> _previouslyClosedPanesAndTabs{};
|
||||
@@ -222,6 +254,8 @@ namespace winrt::TerminalApp::implementation
|
||||
int _renamerLayoutCount{ 0 };
|
||||
bool _renamerPressedEnter{ false };
|
||||
|
||||
std::unordered_map<winrt::guid, winrt::Microsoft::Terminal::Control::ContentProcess> _recentlyDetachedContent{};
|
||||
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowDialogHelper(const std::wstring_view& name);
|
||||
|
||||
void _ShowAboutDialog();
|
||||
@@ -233,13 +267,14 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
void _CreateNewTabFlyout();
|
||||
void _OpenNewTabDropdown();
|
||||
HRESULT _OpenNewTab(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr);
|
||||
HRESULT _OpenNewTab(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs);
|
||||
void _CreateNewTabFromPane(std::shared_ptr<Pane> pane);
|
||||
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(Microsoft::Terminal::Settings::Model::Profile profile, Microsoft::Terminal::Settings::Model::TerminalSettings settings);
|
||||
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(Microsoft::Terminal::Settings::Model::Profile profile,
|
||||
Microsoft::Terminal::Settings::Model::TerminalSettings settings);
|
||||
|
||||
winrt::fire_and_forget _OpenNewWindow(const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs);
|
||||
|
||||
void _OpenNewTerminalViaDropdown(const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs);
|
||||
winrt::fire_and_forget _OpenNewTerminalViaDropdown(const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs);
|
||||
|
||||
bool _displayingCloseDialog{ false };
|
||||
void _SettingsButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
|
||||
@@ -267,7 +302,7 @@ namespace winrt::TerminalApp::implementation
|
||||
void _DuplicateFocusedTab();
|
||||
void _DuplicateTab(const TerminalTab& tab);
|
||||
|
||||
void _SplitTab(TerminalTab& tab);
|
||||
void _SplitTab(winrt::com_ptr<TerminalTab>& tab);
|
||||
winrt::fire_and_forget _ExportTab(const TerminalTab& tab, winrt::hstring filepath);
|
||||
|
||||
winrt::Windows::Foundation::IAsyncAction _HandleCloseTabRequested(winrt::TerminalApp::TabBase tab);
|
||||
@@ -288,7 +323,8 @@ namespace winrt::TerminalApp::implementation
|
||||
bool _SelectTab(uint32_t tabIndex);
|
||||
bool _MoveFocus(const Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
bool _SwapPane(const Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
bool _MovePane(const uint32_t tabIdx);
|
||||
bool _MovePane(const Microsoft::Terminal::Settings::Model::MovePaneArgs args);
|
||||
bool _MoveTab(const Microsoft::Terminal::Settings::Model::MoveTabArgs args);
|
||||
|
||||
template<typename F>
|
||||
bool _ApplyToActiveControls(F f)
|
||||
@@ -324,13 +360,13 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
void _Scroll(ScrollDirection scrollDirection, const Windows::Foundation::IReference<uint32_t>& rowsToScroll);
|
||||
|
||||
void _SplitPane(const Microsoft::Terminal::Settings::Model::SplitDirection splitType,
|
||||
const float splitSize,
|
||||
std::shared_ptr<Pane> newPane);
|
||||
void _SplitPane(TerminalTab& tab,
|
||||
const Microsoft::Terminal::Settings::Model::SplitDirection splitType,
|
||||
const float splitSize,
|
||||
std::shared_ptr<Pane> newPane);
|
||||
void _SplitPaneActiveTab(const Microsoft::Terminal::Settings::Model::SplitDirection splitType,
|
||||
const float splitSize,
|
||||
std::shared_ptr<Pane> newPane);
|
||||
void _SplitPaneOnTab(winrt::com_ptr<TerminalTab>& tab,
|
||||
const Microsoft::Terminal::Settings::Model::SplitDirection splitType,
|
||||
const float splitSize,
|
||||
std::shared_ptr<Pane> newPane);
|
||||
void _ResizePane(const Microsoft::Terminal::Settings::Model::ResizeDirection& direction);
|
||||
void _ToggleSplitOrientation();
|
||||
|
||||
@@ -383,8 +419,6 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
void _RefreshUIForSettingsReload();
|
||||
|
||||
void _SetNonClientAreaColors(const Windows::UI::Color& selectedTabColor);
|
||||
void _ClearNonClientAreaColors();
|
||||
void _SetNewTabButtonColor(const Windows::UI::Color& color, const Windows::UI::Color& accentColor);
|
||||
void _ClearNewTabButtonColor();
|
||||
|
||||
@@ -443,8 +477,48 @@ namespace winrt::TerminalApp::implementation
|
||||
static bool _IsMessageDismissed(const winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage& message);
|
||||
static void _DismissMessage(const winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage& message);
|
||||
|
||||
void _updateThemeColors();
|
||||
|
||||
winrt::fire_and_forget _ShowWindowChangedHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::ShowWindowArgs args);
|
||||
|
||||
PreparedContent _prepareContentProc(const winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs,
|
||||
const bool duplicate);
|
||||
Windows::Foundation::IAsyncOperation<winrt::Microsoft::Terminal::Control::ContentProcess> _CreateNewContentProcess(winrt::Microsoft::Terminal::Settings::Model::Profile profile,
|
||||
winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult settings);
|
||||
Windows::Foundation::IAsyncOperation<winrt::Microsoft::Terminal::Control::ContentProcess> _AttachToContentProcess(const winrt::guid contentGuid);
|
||||
|
||||
winrt::fire_and_forget _createNewTabFromContent(PreparedContent preppedContent,
|
||||
std::function<void(const winrt::com_ptr<TerminalTab>&)> postInitTab = nullptr);
|
||||
|
||||
winrt::fire_and_forget _asyncSplitPaneActiveTab(const Microsoft::Terminal::Settings::Model::SplitDirection splitDirection,
|
||||
const float splitSize,
|
||||
PreparedContent preppedContent);
|
||||
winrt::fire_and_forget _asyncSplitPaneOnTab(winrt::com_ptr<TerminalTab> tab,
|
||||
const Microsoft::Terminal::Settings::Model::SplitDirection splitDirection,
|
||||
const float splitSize,
|
||||
PreparedContent preppedContent);
|
||||
|
||||
std::shared_ptr<Pane> _makePaneFromContent(winrt::Microsoft::Terminal::Control::ContentProcess initContentProc,
|
||||
winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult controlSettings,
|
||||
winrt::Microsoft::Terminal::Settings::Model::Profile profile);
|
||||
|
||||
winrt::Microsoft::Terminal::Control::TermControl _InitControl(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings,
|
||||
const winrt::guid& contentGuid);
|
||||
|
||||
void _evaluateSettings(const winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs,
|
||||
const bool duplicate,
|
||||
winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& controlSettings,
|
||||
winrt::Microsoft::Terminal::Settings::Model::Profile& profile);
|
||||
|
||||
winrt::Microsoft::Terminal::TerminalConnection::ConnectionInformation _CreateConnectionInfoFromSettings(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile,
|
||||
const winrt::Microsoft::Terminal::Settings::Model::TerminalSettings& settings);
|
||||
void _finalizeDetach(winrt::Windows::Foundation::IInspectable sender,
|
||||
winrt::Windows::Foundation::IInspectable e);
|
||||
|
||||
void _onTabDragStarting(winrt::Microsoft::UI::Xaml::Controls::TabView sender, winrt::Microsoft::UI::Xaml::Controls::TabViewTabDragStartingEventArgs e);
|
||||
void _onTabStripDragOver(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::UI::Xaml::DragEventArgs e);
|
||||
winrt::fire_and_forget _onTabStripDrop(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::UI::Xaml::DragEventArgs e);
|
||||
|
||||
#pragma region ActionHandlers
|
||||
// These are all defined in AppActionHandlers.cpp
|
||||
#define ON_ALL_ACTIONS(action) DECLARE_ACTION_HANDLER(action);
|
||||
|
||||
@@ -10,6 +10,12 @@ namespace TerminalApp
|
||||
{
|
||||
String ProposedName { get; };
|
||||
};
|
||||
[default_interface] runtimeclass RequestMoveContentArgs
|
||||
{
|
||||
String Window { get; };
|
||||
String Content { get; };
|
||||
UInt32 TabIndex { get; };
|
||||
};
|
||||
|
||||
interface IDialogPresenter
|
||||
{
|
||||
@@ -44,6 +50,10 @@ namespace TerminalApp
|
||||
String KeyboardServiceDisabledText { get; };
|
||||
|
||||
TaskbarState TaskbarState{ get; };
|
||||
void AttachContent(String content, UInt32 tabIndex);
|
||||
|
||||
Windows.UI.Xaml.Media.Brush TitlebarBrush { get; };
|
||||
void WindowActivated(Boolean activated);
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, String> TitleChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, LastTabClosedEventArgs> LastTabClosed;
|
||||
@@ -57,8 +67,12 @@ namespace TerminalApp
|
||||
event Windows.Foundation.TypedEventHandler<Object, RenameWindowRequestedArgs> RenameWindowRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> IsQuakeWindowChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> SummonWindowRequested;
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> CloseRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> OpenSystemMenu;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Control.ShowWindowArgs> ShowWindowChanged;
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, RequestMoveContentArgs> RequestMoveContent;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,17 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
TerminalTab::TerminalTab(std::shared_ptr<Pane> rootPane)
|
||||
{
|
||||
if (rootPane != nullptr)
|
||||
{
|
||||
AttachRootPane(rootPane);
|
||||
}
|
||||
|
||||
_Setup();
|
||||
}
|
||||
|
||||
void TerminalTab::AttachRootPane(std::shared_ptr<Pane> rootPane)
|
||||
{
|
||||
assert(_rootPane == nullptr);
|
||||
_rootPane = rootPane;
|
||||
_activePane = nullptr;
|
||||
|
||||
@@ -60,7 +71,13 @@ namespace winrt::TerminalApp::implementation
|
||||
_mruPanes.insert(_mruPanes.begin(), id.value());
|
||||
}
|
||||
|
||||
_Setup();
|
||||
_rootClosedToken = _rootPane->Closed([=](auto&& /*s*/, auto&& /*e*/) {
|
||||
_ClosedHandlers(nullptr, nullptr);
|
||||
});
|
||||
|
||||
Content(_rootPane->GetRootElement());
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -71,12 +88,6 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
void TerminalTab::_Setup()
|
||||
{
|
||||
_rootClosedToken = _rootPane->Closed([=](auto&& /*s*/, auto&& /*e*/) {
|
||||
_ClosedHandlers(nullptr, nullptr);
|
||||
});
|
||||
|
||||
Content(_rootPane->GetRootElement());
|
||||
|
||||
_MakeTabViewItem();
|
||||
_CreateContextMenu();
|
||||
|
||||
@@ -379,6 +390,10 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
return _runtimeTabText;
|
||||
}
|
||||
if (!_activePane)
|
||||
{
|
||||
return L"";
|
||||
}
|
||||
if (!_activePane->_IsLeaf())
|
||||
{
|
||||
return RS_(L"MultiplePanes");
|
||||
@@ -437,16 +452,16 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - A vector of commands
|
||||
std::vector<ActionAndArgs> TerminalTab::BuildStartupActions() const
|
||||
std::vector<ActionAndArgs> TerminalTab::BuildStartupActions(const bool asContent) const
|
||||
{
|
||||
// Give initial ids (0 for the child created with this tab,
|
||||
// 1 for the child after the first split.
|
||||
auto state = _rootPane->BuildStartupActions(0, 1);
|
||||
auto state = _rootPane->BuildStartupActions(0, 1, asContent);
|
||||
|
||||
{
|
||||
ActionAndArgs newTabAction{};
|
||||
newTabAction.Action(ShortcutAction::NewTab);
|
||||
NewTabArgs newTabArgs{ state.firstPane->GetTerminalArgsForPane() };
|
||||
NewTabArgs newTabArgs{ state.firstPane->GetTerminalArgsForPane(asContent) };
|
||||
newTabAction.Args(newTabArgs);
|
||||
|
||||
state.args.emplace(state.args.begin(), std::move(newTabAction));
|
||||
@@ -742,6 +757,8 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
bool TerminalTab::FocusPane(const uint32_t id)
|
||||
{
|
||||
if (_rootPane == nullptr)
|
||||
return false;
|
||||
_changingActivePane = true;
|
||||
const auto res = _rootPane->FocusPane(id);
|
||||
_changingActivePane = false;
|
||||
@@ -864,7 +881,7 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
});
|
||||
|
||||
events.taskbarToken = control.SetTaskbarProgress([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget {
|
||||
events.taskbarToken = control.SetTaskbarProgress([dispatcher, weakThis](auto&&, auto &&) -> winrt::fire_and_forget {
|
||||
co_await wil::resume_foreground(dispatcher);
|
||||
// Check if Tab's lifetime has expired
|
||||
if (auto tab{ weakThis.get() })
|
||||
@@ -1069,7 +1086,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// Add a Closed event handler to the Pane. If the pane closes out from
|
||||
// underneath us, and it's zoomed, we want to be able to make sure to
|
||||
// update our state accordingly to un-zoom that pane. See GH#7252.
|
||||
auto closedToken = pane->Closed([weakThis, weakPane](auto&& /*s*/, auto&& /*e*/) -> winrt::fire_and_forget {
|
||||
auto closedToken = pane->Closed([weakThis, weakPane](auto&& /*s*/, auto && /*e*/) -> winrt::fire_and_forget {
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
if (tab->_zoomedPane)
|
||||
@@ -1343,7 +1360,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// -------------------- | -- | --
|
||||
// Runtime Color | _optional_ | Color Picker / `setTabColor` action
|
||||
// Control Tab Color | _optional_ | Profile's `tabColor`, or a color set by VT
|
||||
// Theme Tab Background | _optional_ | `tab.backgroundColor` in the theme
|
||||
// Theme Tab Background | _optional_ | `tab.backgroundColor` in the theme (handled in _RecalculateAndApplyTabColor)
|
||||
// Tab Default Color | **default** | TabView in XAML
|
||||
//
|
||||
// coalesce will get us the first of these values that's
|
||||
@@ -1352,7 +1369,6 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
return til::coalesce(_runtimeTabColor,
|
||||
controlTabColor,
|
||||
_themeTabColor,
|
||||
std::optional<Windows::UI::Color>(std::nullopt));
|
||||
}
|
||||
|
||||
@@ -1370,6 +1386,12 @@ namespace winrt::TerminalApp::implementation
|
||||
_RecalculateAndApplyTabColor();
|
||||
}
|
||||
|
||||
void TerminalTab::ThemeColor(const winrt::Microsoft::Terminal::Settings::Model::ThemeColor& color)
|
||||
{
|
||||
_themeColor = color;
|
||||
_RecalculateAndApplyTabColor();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - This function dispatches a function to the UI thread to recalculate
|
||||
// what this tab's current background color should be. If a color is set,
|
||||
@@ -1390,11 +1412,37 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
auto tab{ ptrTab };
|
||||
|
||||
// GetTabColor will return the color set by the color picker, or the
|
||||
// color specified in the profile. If neither of those were set,
|
||||
// then look to _themeColor to see if there's a value there.
|
||||
// Otherwise, clear our color, falling back to the TabView defaults.
|
||||
auto currentColor = tab->GetTabColor();
|
||||
if (currentColor.has_value())
|
||||
{
|
||||
tab->_ApplyTabColor(currentColor.value());
|
||||
}
|
||||
else if (tab->_themeColor != nullptr)
|
||||
{
|
||||
// One-liner to safely get the active control's brush.
|
||||
Media::Brush terminalBrush{ nullptr };
|
||||
if (const auto& c{ tab->GetActiveTerminalControl() })
|
||||
{
|
||||
terminalBrush = c.BackgroundBrush();
|
||||
}
|
||||
|
||||
if (const auto themeBrush{ tab->_themeColor.Evaluate(Application::Current().Resources(), terminalBrush, false) })
|
||||
{
|
||||
// ThemeColor.Evaluate will get us a Brush (because the
|
||||
// TermControl could have an acrylic BG, for example). Take
|
||||
// that brush, and get the color out of it. We don't really
|
||||
// want to have the tab items themselves be acrylic.
|
||||
tab->_ApplyTabColor(til::color{ ThemeColor::ColorFromBrush(themeBrush) });
|
||||
}
|
||||
else
|
||||
{
|
||||
tab->_ClearTabBackgroundColor();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tab->_ClearTabBackgroundColor();
|
||||
@@ -1452,18 +1500,16 @@ namespace winrt::TerminalApp::implementation
|
||||
subtleFillColorTertiaryBrush.Color(subtleFillColorTertiary);
|
||||
}
|
||||
|
||||
hoverTabBrush.Color(TerminalApp::ColorHelper::GetAccentColor(color));
|
||||
selectedTabBrush.Color(color);
|
||||
|
||||
// currently if a tab has a custom color, a deselected state is
|
||||
// signified by using the same color with a bit of transparency
|
||||
auto deselectedTabColor = color;
|
||||
deselectedTabColor.A = 64;
|
||||
deselectedTabBrush.Color(deselectedTabColor);
|
||||
deselectedTabBrush.Color(color);
|
||||
deselectedTabBrush.Opacity(0.3);
|
||||
|
||||
hoverTabBrush.Color(color);
|
||||
hoverTabBrush.Opacity(0.6);
|
||||
|
||||
// currently if a tab has a custom color, a deselected state is
|
||||
// signified by using the same color with a bit of transparency
|
||||
//
|
||||
// Prior to MUX 2.7, we set TabViewItemHeaderBackground, but now we can
|
||||
// use TabViewItem().Background() for that. HOWEVER,
|
||||
// TabViewItem().Background() only sets the color of the tab background
|
||||
|
||||
@@ -36,6 +36,7 @@ namespace winrt::TerminalApp::implementation
|
||||
std::shared_ptr<Pane> DetachRoot();
|
||||
std::shared_ptr<Pane> DetachPane();
|
||||
void AttachPane(std::shared_ptr<Pane> pane);
|
||||
void AttachRootPane(std::shared_ptr<Pane> rootPane);
|
||||
|
||||
void SplitPane(winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType,
|
||||
const float splitSize,
|
||||
@@ -71,6 +72,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
std::optional<winrt::Windows::UI::Color> GetTabColor();
|
||||
|
||||
void ThemeColor(const winrt::Microsoft::Terminal::Settings::Model::ThemeColor& color);
|
||||
void SetRuntimeTabColor(const winrt::Windows::UI::Color& color);
|
||||
void ResetRuntimeTabColor();
|
||||
void ActivateColorPicker();
|
||||
@@ -81,7 +83,7 @@ namespace winrt::TerminalApp::implementation
|
||||
void EnterZoom();
|
||||
void ExitZoom();
|
||||
|
||||
std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions() const override;
|
||||
std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions(const bool asContent = false) const override;
|
||||
|
||||
int GetLeafPaneCount() const noexcept;
|
||||
|
||||
@@ -113,10 +115,10 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
winrt::hstring _lastIconPath{};
|
||||
winrt::TerminalApp::ColorPickupFlyout _tabColorPickup{};
|
||||
std::optional<winrt::Windows::UI::Color> _themeTabColor{};
|
||||
std::optional<winrt::Windows::UI::Color> _runtimeTabColor{};
|
||||
winrt::TerminalApp::TabHeaderControl _headerControl{};
|
||||
winrt::TerminalApp::TerminalTabStatus _tabStatus{};
|
||||
winrt::Microsoft::Terminal::Settings::Model::ThemeColor _themeColor{ nullptr };
|
||||
|
||||
struct ControlEventTokens
|
||||
{
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#include "TitlebarControl.h"
|
||||
|
||||
#include "ColorHelper.h"
|
||||
|
||||
#include "TitlebarControl.g.cpp"
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
@@ -21,6 +23,25 @@ namespace winrt::TerminalApp::implementation
|
||||
MinMaxCloseControl().MinimizeClick({ this, &TitlebarControl::Minimize_Click });
|
||||
MinMaxCloseControl().MaximizeClick({ this, &TitlebarControl::Maximize_Click });
|
||||
MinMaxCloseControl().CloseClick({ this, &TitlebarControl::Close_Click });
|
||||
|
||||
// Listen for changes to the Background. If the Background changes,
|
||||
// we'll want to manually adjust the RequestedTheme of our caption
|
||||
// buttons, so the foreground stands out against whatever BG color was
|
||||
// selected for us.
|
||||
//
|
||||
// This is how you register a PropertyChanged event for the Background
|
||||
// property of a Grid. The Background property is defined in the base
|
||||
// class Panel.
|
||||
const auto bgProperty{ winrt::Windows::UI::Xaml::Controls::Panel::BackgroundProperty() };
|
||||
RegisterPropertyChangedCallback(bgProperty, [weakThis = get_weak(), bgProperty](auto& /*sender*/, auto& e) {
|
||||
if (auto self{ weakThis.get() })
|
||||
{
|
||||
if (e == bgProperty)
|
||||
{
|
||||
self->_backgroundChanged(self->Background());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
double TitlebarControl::CaptionButtonWidth()
|
||||
@@ -144,4 +165,26 @@ namespace winrt::TerminalApp::implementation
|
||||
MinMaxCloseControl().ReleaseButtons();
|
||||
}
|
||||
|
||||
void TitlebarControl::_backgroundChanged(winrt::Windows::UI::Xaml::Media::Brush brush)
|
||||
{
|
||||
// Loosely cribbed from TerminalPage::_SetNewTabButtonColor
|
||||
til::color c;
|
||||
if (auto acrylic = brush.try_as<winrt::Windows::UI::Xaml::Media::AcrylicBrush>())
|
||||
{
|
||||
c = acrylic.TintColor();
|
||||
}
|
||||
else if (auto solidColor = brush.try_as<winrt::Windows::UI::Xaml::Media::SolidColorBrush>())
|
||||
{
|
||||
c = solidColor.Color();
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto isBrightColor = ColorHelper::IsBrightColor(c);
|
||||
MinMaxCloseControl().RequestedTheme(isBrightColor ? winrt::Windows::UI::Xaml::ElementTheme::Light :
|
||||
winrt::Windows::UI::Xaml::ElementTheme::Dark);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ namespace winrt::TerminalApp::implementation
|
||||
private:
|
||||
void _OnMaximizeOrRestore(byte flag);
|
||||
HWND _window{ nullptr }; // non-owning handle; should not be freed in the dtor.
|
||||
|
||||
void _backgroundChanged(winrt::Windows::UI::Xaml::Media::Brush brush);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
VerticalAlignment="Top"
|
||||
d:DesignHeight="36"
|
||||
d:DesignWidth="400"
|
||||
Background="{ThemeResource TabViewBackground}"
|
||||
SizeChanged="Root_SizeChanged"
|
||||
mc:Ignorable="d">
|
||||
|
||||
|
||||
@@ -53,7 +53,12 @@ CATCH_RETURN()
|
||||
HRESULT CTerminalHandoff::s_StopListening()
|
||||
{
|
||||
std::unique_lock lock{ _mtx };
|
||||
return s_StopListeningLocked();
|
||||
}
|
||||
|
||||
// See s_StopListening()
|
||||
HRESULT CTerminalHandoff::s_StopListeningLocked()
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_NOT_VALID_STATE, _pfnHandoff);
|
||||
|
||||
_pfnHandoff = nullptr;
|
||||
@@ -101,14 +106,16 @@ HRESULT CTerminalHandoff::EstablishPtyHandoff(HANDLE in, HANDLE out, HANDLE sign
|
||||
{
|
||||
try
|
||||
{
|
||||
// Stash a local copy of _pfnHandoff before we stop listening.
|
||||
std::unique_lock lock{ _mtx };
|
||||
|
||||
// s_StopListeningLocked sets _pfnHandoff to nullptr.
|
||||
// localPfnHandoff is tested for nullness below.
|
||||
#pragma warning(suppress : 26429) // Symbol '...' is never tested for nullness, it can be marked as not_null (f.23).
|
||||
auto localPfnHandoff = _pfnHandoff;
|
||||
|
||||
// Because we are REGCLS_SINGLEUSE... we need to `CoRevokeClassObject` after we handle this ONE call.
|
||||
// COM does not automatically clean that up for us. We must do it.
|
||||
s_StopListening();
|
||||
|
||||
std::unique_lock lock{ _mtx };
|
||||
LOG_IF_FAILED(s_StopListeningLocked());
|
||||
|
||||
// Report an error if no one registered a handoff function before calling this.
|
||||
THROW_HR_IF_NULL(E_NOT_VALID_STATE, localPfnHandoff);
|
||||
|
||||
@@ -43,6 +43,9 @@ struct __declspec(uuid(__CLSID_CTerminalHandoff))
|
||||
|
||||
static HRESULT s_StartListening(NewHandoffFunction pfnHandoff);
|
||||
static HRESULT s_StopListening();
|
||||
|
||||
private:
|
||||
static HRESULT s_StopListeningLocked();
|
||||
};
|
||||
|
||||
// Disable warnings from the CoCreatableClass macro as the value it provides for
|
||||
|
||||
@@ -36,21 +36,59 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||
auto raw = reinterpret_cast<::IInspectable**>(pointer);
|
||||
#pragma warning(pop)
|
||||
|
||||
// RoActivateInstance() will try to create an instance of the object,
|
||||
// who's fully qualified name is the string in Name().
|
||||
TerminalConnection::ITerminalConnection connection{ nullptr };
|
||||
|
||||
// A couple short-circuits, for connections that _we_ implement.
|
||||
// Sometimes, RoActivateInstance is weird and fails with errors like the
|
||||
// following
|
||||
//
|
||||
// The class has to be activatable. For the Terminal, this is easy
|
||||
// enough - we're not hosting anything that's not already in our
|
||||
// manifest, or living as a .dll & .winmd SxS.
|
||||
//
|
||||
// When we get to extensions (GH#4000), we may want to revisit.
|
||||
if (LOG_IF_FAILED(RoActivateInstance(name, raw)))
|
||||
/*
|
||||
onecore\com\combase\inc\RegistryKey.hpp(527)\combase.dll!01234:
|
||||
(caller: 01234) LogHr(2) tid(83a8) 800700A1 The specified
|
||||
path is invalid.
|
||||
Msg:[StaticNtOpen failed with
|
||||
path:\REGISTRY\A\{A41685A4-AD85-4C4C-BA5D-A849ADBF3C40}\ActivatableClassId
|
||||
\REGISTRY\MACHINE\Software\Classes\ActivatableClasses]
|
||||
...\src\cascadia\TerminalConnection\ConnectionInformation.cpp(47)\TerminalConnection.dll!01234:
|
||||
(caller: 01234) LogHr(1) tid(83a8) 800700A1 The specified
|
||||
path is invalid.
|
||||
[...TerminalConnection::implementation::ConnectionInformation::CreateConnection(RoActivateInstance(name,
|
||||
raw))]
|
||||
*/
|
||||
//
|
||||
// So to avoid those, we'll manually instantiate these
|
||||
if (info.ClassName() == winrt::name_of<TerminalConnection::ConptyConnection>())
|
||||
{
|
||||
return nullptr;
|
||||
connection = TerminalConnection::ConptyConnection();
|
||||
}
|
||||
else if (info.ClassName() == winrt::name_of<TerminalConnection::AzureConnection>())
|
||||
{
|
||||
connection = TerminalConnection::AzureConnection();
|
||||
}
|
||||
else if (info.ClassName() == winrt::name_of<TerminalConnection::EchoConnection>())
|
||||
{
|
||||
connection = TerminalConnection::EchoConnection();
|
||||
}
|
||||
else
|
||||
{
|
||||
// RoActivateInstance() will try to create an instance of the object,
|
||||
// who's fully qualified name is the string in Name().
|
||||
//
|
||||
// The class has to be activatable. For the Terminal, this is easy
|
||||
// enough - we're not hosting anything that's not already in our
|
||||
// manifest, or living as a .dll & .winmd SxS.
|
||||
//
|
||||
// When we get to extensions (GH#4000), we may want to revisit.
|
||||
if (LOG_IF_FAILED(RoActivateInstance(name, raw)))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
connection = inspectable.try_as<TerminalConnection::ITerminalConnection>();
|
||||
}
|
||||
|
||||
// Now that thing we made, make sure it's actually a ITerminalConnection
|
||||
if (const auto connection{ inspectable.try_as<TerminalConnection::ITerminalConnection>() })
|
||||
if (connection)
|
||||
{
|
||||
// Initialize it, and return it.
|
||||
connection.Initialize(info.Settings());
|
||||
|
||||
147
src/cascadia/TerminalControl/ContentProcess.cpp
Normal file
147
src/cascadia/TerminalControl/ContentProcess.cpp
Normal file
@@ -0,0 +1,147 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "ContentProcess.h"
|
||||
#include "ContentProcess.g.cpp"
|
||||
#include "ControlCore.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
ContentProcess::ContentProcess(winrt::guid g) :
|
||||
_ourPID{ GetCurrentProcessId() },
|
||||
_guid{ g } {}
|
||||
|
||||
bool ContentProcess::Initialize(Control::IControlSettings settings,
|
||||
Control::IControlAppearance unfocusedAppearance,
|
||||
TerminalConnection::ConnectionInformation connectionInfo)
|
||||
{
|
||||
auto conn{ TerminalConnection::ConnectionInformation::CreateConnection(connectionInfo) };
|
||||
if (conn == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_interactivity = winrt::make<implementation::ControlInteractivity>(settings, unfocusedAppearance, conn);
|
||||
return true;
|
||||
}
|
||||
|
||||
ContentProcess::~ContentProcess()
|
||||
{
|
||||
}
|
||||
|
||||
// See https://docs.microsoft.com/en-us/windows/uwp/cpp-and-winrt-apis/details-about-destructors#deferred-destruction
|
||||
winrt::fire_and_forget ContentProcess::final_release(std::unique_ptr<ContentProcess> ptr) noexcept
|
||||
{
|
||||
winrt::com_ptr<ControlCore> coreImpl;
|
||||
coreImpl.copy_from(winrt::get_self<ControlCore>(ptr->_interactivity.Core()));
|
||||
if (coreImpl)
|
||||
{
|
||||
// Close() requires that it is called on the "main" thread. So we
|
||||
// need to switch over to the DIspatcher thread, before calling
|
||||
// Close.
|
||||
co_await wil::resume_foreground(coreImpl->Dispatcher(), winrt::Windows::System::DispatcherQueuePriority::Normal);
|
||||
|
||||
// Typically, Close() runs async, closing the connection on a BG
|
||||
// thread, so that the UI doesn't hang while waiting for the client
|
||||
// process to exit. When we're running as a content process, that's
|
||||
// not relevant. In that case, we need to close the connection NOW,
|
||||
// because we're about to exit the whole process. If we close the
|
||||
// process asynchronously (on a bg thread), then we might
|
||||
// accidentally leak it as we exit() before the thread gets a time
|
||||
// slice.
|
||||
coreImpl->Close(false);
|
||||
}
|
||||
|
||||
// DANGER - We're straight up going to EXIT THE ENTIRE PROCESS when we
|
||||
// get destructed. This eliminates the need to do any sort of
|
||||
// ref-counting weirdness. This entire process exists to host one
|
||||
// singular ContentProcess instance. When we're destructed, it's because
|
||||
// every other window process was done with us. We can die now, knowing
|
||||
// that our job is complete.
|
||||
|
||||
std::exit(0);
|
||||
}
|
||||
|
||||
Control::ControlInteractivity ContentProcess::GetInteractivity()
|
||||
{
|
||||
return _interactivity;
|
||||
}
|
||||
|
||||
uint64_t ContentProcess::GetPID()
|
||||
{
|
||||
return _ourPID;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Duplicate the swap chain handle to the provided process.
|
||||
// - If the provided PID is our pid, then great - we don't need to do anything.
|
||||
// Arguments:
|
||||
// - callersPid: the PID of the process calling this method.
|
||||
// Return Value:
|
||||
// - The value of the swapchain handle in the callers process
|
||||
// Notes:
|
||||
// - This is BODGY! We're basically asking to marshal a HANDLE here. WinRT
|
||||
// has no good mechanism for doing this, so we're doing it by casting the
|
||||
// value to a uint64_t. In all reality, we _should_ be using a COM
|
||||
// interface for this, because it can set up the security on these handles
|
||||
// more appropriately. Fortunately, all we're dealing with is swapchains,
|
||||
// so the security doesn't matter all that much.
|
||||
uint64_t ContentProcess::RequestSwapChainHandle(const uint64_t callersPid)
|
||||
{
|
||||
auto ourPid = GetCurrentProcessId();
|
||||
HANDLE ourHandle = reinterpret_cast<HANDLE>(_interactivity.Core().SwapChainHandle());
|
||||
if (callersPid == ourPid)
|
||||
{
|
||||
return reinterpret_cast<uint64_t>(ourHandle);
|
||||
}
|
||||
|
||||
wil::unique_handle hWindowProcess{ OpenProcess(PROCESS_ALL_ACCESS,
|
||||
FALSE,
|
||||
static_cast<DWORD>(callersPid)) };
|
||||
if (hWindowProcess.get() == nullptr)
|
||||
{
|
||||
TraceLoggingWrite(g_hTerminalControlProvider,
|
||||
"ContentProcess::RequestSwapChainHandle_OpenOtherProcessFailed",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
|
||||
LOG_LAST_ERROR();
|
||||
return 0;
|
||||
}
|
||||
|
||||
HANDLE theirHandle{ nullptr };
|
||||
BOOL success = DuplicateHandle(GetCurrentProcess(),
|
||||
ourHandle,
|
||||
hWindowProcess.get(),
|
||||
&theirHandle,
|
||||
0,
|
||||
FALSE,
|
||||
DUPLICATE_SAME_ACCESS);
|
||||
if (!success)
|
||||
{
|
||||
TraceLoggingWrite(g_hTerminalControlProvider,
|
||||
"ContentProcess::RequestSwapChainHandle_DuplicateHandleFailed",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
|
||||
LOG_LAST_ERROR();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// At this point, the handle is now in their process space, with value
|
||||
// theirHandle
|
||||
return reinterpret_cast<uint64_t>(theirHandle);
|
||||
}
|
||||
|
||||
winrt::guid ContentProcess::Guid()
|
||||
{
|
||||
return _guid;
|
||||
}
|
||||
|
||||
void ContentProcess::Attach()
|
||||
{
|
||||
// TODO! This feels like a hack and I'm sure cppwinrt gives us some sort
|
||||
// of hook for when we get an incremented refcount
|
||||
_AttachedHandlers(*this, nullptr);
|
||||
}
|
||||
}
|
||||
39
src/cascadia/TerminalControl/ContentProcess.h
Normal file
39
src/cascadia/TerminalControl/ContentProcess.h
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ContentProcess.g.h"
|
||||
#include "ControlInteractivity.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
struct ContentProcess : ContentProcessT<ContentProcess>
|
||||
{
|
||||
ContentProcess(winrt::guid g);
|
||||
~ContentProcess();
|
||||
static winrt::fire_and_forget final_release(std::unique_ptr<ContentProcess> ptr) noexcept;
|
||||
|
||||
bool Initialize(Control::IControlSettings settings,
|
||||
Control::IControlAppearance unfocusedAppearance,
|
||||
TerminalConnection::ConnectionInformation connectionInfo);
|
||||
Control::ControlInteractivity GetInteractivity();
|
||||
|
||||
uint64_t GetPID();
|
||||
uint64_t RequestSwapChainHandle(const uint64_t pid);
|
||||
winrt::guid Guid();
|
||||
|
||||
void Attach();
|
||||
TYPED_EVENT(Attached, IInspectable, IInspectable);
|
||||
|
||||
private:
|
||||
Control::ControlInteractivity _interactivity{ nullptr };
|
||||
uint64_t _ourPID;
|
||||
winrt::guid _guid;
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Control::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(ContentProcess);
|
||||
}
|
||||
28
src/cascadia/TerminalControl/ContentProcess.idl
Normal file
28
src/cascadia/TerminalControl/ContentProcess.idl
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import "ControlInteractivity.idl";
|
||||
|
||||
namespace Microsoft.Terminal.Control
|
||||
{
|
||||
runtimeclass ContentProcess {
|
||||
|
||||
ContentProcess(Guid g);
|
||||
|
||||
Boolean Initialize(IControlSettings settings,
|
||||
IControlAppearance unfocusedAppearance,
|
||||
Microsoft.Terminal.TerminalConnection.ConnectionInformation connectionInfo);
|
||||
|
||||
ControlInteractivity GetInteractivity();
|
||||
|
||||
UInt64 GetPID();
|
||||
|
||||
UInt64 RequestSwapChainHandle(UInt64 pid);
|
||||
|
||||
Guid Guid { get; };
|
||||
|
||||
void Attach();
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> Attached;
|
||||
|
||||
};
|
||||
}
|
||||
@@ -218,6 +218,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
UpdateSettings(settings, unfocusedAppearance);
|
||||
}
|
||||
|
||||
winrt::Windows::System::DispatcherQueue ControlCore::Dispatcher()
|
||||
{
|
||||
return _dispatcher;
|
||||
}
|
||||
|
||||
ControlCore::~ControlCore()
|
||||
{
|
||||
Close();
|
||||
@@ -417,12 +422,43 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
vkey != VK_SNAPSHOT &&
|
||||
keyDown)
|
||||
{
|
||||
if (_terminal->IsInMarkMode() && modifiers.IsCtrlPressed() && vkey == 'A')
|
||||
const auto isInMarkMode = _terminal->SelectionMode() == ::Terminal::SelectionInteractionMode::Mark;
|
||||
if (isInMarkMode)
|
||||
{
|
||||
auto lock = _terminal->LockForWriting();
|
||||
_terminal->SelectAll();
|
||||
_renderer->TriggerSelection();
|
||||
return true;
|
||||
if (modifiers.IsCtrlPressed() && vkey == 'A')
|
||||
{
|
||||
// Ctrl + A --> Select all
|
||||
auto lock = _terminal->LockForWriting();
|
||||
_terminal->SelectAll();
|
||||
_updateSelectionUI();
|
||||
return true;
|
||||
}
|
||||
else if (vkey == VK_TAB && _settings->DetectURLs())
|
||||
{
|
||||
// [Shift +] Tab --> next/previous hyperlink
|
||||
auto lock = _terminal->LockForWriting();
|
||||
const auto direction = modifiers.IsShiftPressed() ? ::Terminal::SearchDirection::Backward : ::Terminal::SearchDirection::Forward;
|
||||
_terminal->SelectHyperlink(direction);
|
||||
_updateSelectionUI();
|
||||
return true;
|
||||
}
|
||||
else if (vkey == VK_RETURN && modifiers.IsCtrlPressed() && _terminal->IsTargetingUrl())
|
||||
{
|
||||
// Ctrl + Enter --> Open URL
|
||||
auto lock = _terminal->LockForReading();
|
||||
const auto uri = _terminal->GetHyperlinkAtBufferPosition(_terminal->GetSelectionAnchor());
|
||||
_OpenHyperlinkHandlers(*this, winrt::make<OpenHyperlinkEventArgs>(winrt::hstring{ uri }));
|
||||
return true;
|
||||
}
|
||||
else if (vkey == VK_RETURN)
|
||||
{
|
||||
// [Shift +] Enter --> copy text
|
||||
// Don't lock here! CopySelectionToClipboard already locks for you!
|
||||
CopySelectionToClipboard(modifiers.IsShiftPressed(), nullptr);
|
||||
_terminal->ClearSelection();
|
||||
_updateSelectionUI();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// try to update the selection
|
||||
@@ -430,7 +466,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
auto lock = _terminal->LockForWriting();
|
||||
_terminal->UpdateSelection(updateSlnParams->first, updateSlnParams->second, modifiers);
|
||||
_renderer->TriggerSelection();
|
||||
_updateSelectionUI();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -438,7 +474,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
if (!modifiers.IsWinPressed())
|
||||
{
|
||||
_terminal->ClearSelection();
|
||||
_renderer->TriggerSelection();
|
||||
_updateSelectionUI();
|
||||
}
|
||||
|
||||
// When there is a selection active, escape should clear it and NOT flow through
|
||||
@@ -590,12 +626,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_lastHoveredCell = terminalPosition;
|
||||
uint16_t newId{ 0u };
|
||||
// we can't use auto here because we're pre-declaring newInterval.
|
||||
decltype(_terminal->GetHyperlinkIntervalFromPosition({})) newInterval{ std::nullopt };
|
||||
decltype(_terminal->GetHyperlinkIntervalFromViewportPosition({})) newInterval{ std::nullopt };
|
||||
if (terminalPosition.has_value())
|
||||
{
|
||||
auto lock = _terminal->LockForReading(); // Lock for the duration of our reads.
|
||||
newId = _terminal->GetHyperlinkIdAtPosition(*terminalPosition);
|
||||
newInterval = _terminal->GetHyperlinkIntervalFromPosition(*terminalPosition);
|
||||
newId = _terminal->GetHyperlinkIdAtViewportPosition(*terminalPosition);
|
||||
newInterval = _terminal->GetHyperlinkIntervalFromViewportPosition(*terminalPosition);
|
||||
}
|
||||
|
||||
// If the hyperlink ID changed or the interval changed, trigger a redraw all
|
||||
@@ -625,7 +661,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
// Lock for the duration of our reads.
|
||||
auto lock = _terminal->LockForReading();
|
||||
return winrt::hstring{ _terminal->GetHyperlinkAtPosition(til::point{ pos }) };
|
||||
return winrt::hstring{ _terminal->GetHyperlinkAtViewportPosition(til::point{ pos }) };
|
||||
}
|
||||
|
||||
winrt::hstring ControlCore::HoveredUriText() const
|
||||
@@ -633,7 +669,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
auto lock = _terminal->LockForReading(); // Lock for the duration of our reads.
|
||||
if (_lastHoveredCell.has_value())
|
||||
{
|
||||
return winrt::hstring{ _terminal->GetHyperlinkAtPosition(*_lastHoveredCell) };
|
||||
return winrt::hstring{ _terminal->GetHyperlinkAtViewportPosition(*_lastHoveredCell) };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -934,6 +970,30 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_terminal->SetSelectionAnchor(position);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Retrieves selection metadata from Terminal necessary to draw the
|
||||
// selection markers.
|
||||
// - Since all of this needs to be done under lock, it is more performant
|
||||
// to throw it all in a struct and pass it along.
|
||||
Control::SelectionData ControlCore::SelectionInfo() const
|
||||
{
|
||||
auto lock = _terminal->LockForReading();
|
||||
Control::SelectionData info;
|
||||
|
||||
const auto start{ _terminal->SelectionStartForRendering() };
|
||||
info.StartPos = { start.X, start.Y };
|
||||
|
||||
const auto end{ _terminal->SelectionEndForRendering() };
|
||||
info.EndPos = { end.X, end.Y };
|
||||
|
||||
info.Endpoint = static_cast<SelectionEndpointTarget>(_terminal->SelectionEndpointTarget());
|
||||
|
||||
const auto bufferSize{ _terminal->GetTextBuffer().GetSize() };
|
||||
info.StartAtLeftBoundary = _terminal->GetSelectionAnchor().x == bufferSize.Left();
|
||||
info.EndAtRightBoundary = _terminal->GetSelectionEnd().x == bufferSize.RightInclusive();
|
||||
return info;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Sets selection's end position to match supplied cursor position, e.g. while mouse dragging.
|
||||
// Arguments:
|
||||
@@ -956,7 +1016,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
// save location (for rendering) + render
|
||||
_terminal->SetSelectionEnd(terminalPosition);
|
||||
_renderer->TriggerSelection();
|
||||
_updateSelectionUI();
|
||||
}
|
||||
|
||||
// Called when the Terminal wants to set something to the clipboard, i.e.
|
||||
@@ -969,7 +1029,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// Method Description:
|
||||
// - Given a copy-able selection, get the selected text from the buffer and send it to the
|
||||
// Windows Clipboard (CascadiaWin32:main.cpp).
|
||||
// - CopyOnSelect does NOT clear the selection
|
||||
// Arguments:
|
||||
// - singleLine: collapse all of the text to one line
|
||||
// - formats: which formats to copy (defined by action's CopyFormatting arg). nullptr
|
||||
@@ -1015,12 +1074,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
bgColor) :
|
||||
"";
|
||||
|
||||
if (!_settings->CopyOnSelect())
|
||||
{
|
||||
_terminal->ClearSelection();
|
||||
_renderer->TriggerSelection();
|
||||
}
|
||||
|
||||
// send data up for clipboard
|
||||
_CopyToClipboardHandlers(*this,
|
||||
winrt::make<CopyToClipboardEventArgs>(winrt::hstring{ textData },
|
||||
@@ -1034,7 +1087,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
auto lock = _terminal->LockForWriting();
|
||||
_terminal->SelectAll();
|
||||
_renderer->TriggerSelection();
|
||||
_updateSelectionUI();
|
||||
}
|
||||
|
||||
void ControlCore::ClearSelection()
|
||||
{
|
||||
auto lock = _terminal->LockForWriting();
|
||||
_terminal->ClearSelection();
|
||||
_updateSelectionUI();
|
||||
}
|
||||
|
||||
bool ControlCore::ToggleBlockSelection()
|
||||
@@ -1044,6 +1104,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
_terminal->SetBlockSelection(!_terminal->IsBlockSelection());
|
||||
_renderer->TriggerSelection();
|
||||
// do not update the selection markers!
|
||||
// if we were showing them, keep it that way.
|
||||
// otherwise, continue to not show them
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1053,12 +1116,23 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
auto lock = _terminal->LockForWriting();
|
||||
_terminal->ToggleMarkMode();
|
||||
_renderer->TriggerSelection();
|
||||
_updateSelectionUI();
|
||||
}
|
||||
|
||||
bool ControlCore::IsInMarkMode() const
|
||||
Control::SelectionInteractionMode ControlCore::SelectionMode() const
|
||||
{
|
||||
return _terminal->IsInMarkMode();
|
||||
return static_cast<Control::SelectionInteractionMode>(_terminal->SelectionMode());
|
||||
}
|
||||
|
||||
bool ControlCore::SwitchSelectionEndpoint()
|
||||
{
|
||||
if (_terminal->IsSelectionActive())
|
||||
{
|
||||
_terminal->SwitchSelectionEndpoint();
|
||||
_updateSelectionUI();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -1068,7 +1142,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
_terminal->WritePastedText(hstr);
|
||||
_terminal->ClearSelection();
|
||||
_renderer->TriggerSelection();
|
||||
_updateSelectionUI();
|
||||
_terminal->TrySnapOnInput();
|
||||
}
|
||||
|
||||
@@ -1274,6 +1348,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// - duration - How long the note should be sustained (in microseconds).
|
||||
void ControlCore::_terminalPlayMidiNote(const int noteNumber, const int velocity, const std::chrono::microseconds duration)
|
||||
{
|
||||
// TODO! GH#1256 This is intentionally here to conflict with https://github.com/microsoft/terminal/pull/13471#pullrequestreview-1039353718
|
||||
// When we do tearout, we'll need to also recreate the midi thing
|
||||
|
||||
// We create the audio instance on demand, and lock it for the duration
|
||||
// of the note output so it can't be destroyed while in use.
|
||||
auto& midiAudio = _getMidiAudio();
|
||||
@@ -1388,7 +1465,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
_terminal->SetBlockSelection(false);
|
||||
search.Select();
|
||||
|
||||
// this is used for search,
|
||||
// DO NOT call _updateSelectionUI() here.
|
||||
// We don't want to show the markers so manually tell it to clear it.
|
||||
_renderer->TriggerSelection();
|
||||
_UpdateSelectionMarkersHandlers(*this, winrt::make<implementation::UpdateSelectionMarkersEventArgs>(true));
|
||||
}
|
||||
|
||||
// Raise a FoundMatch event, which the control will use to notify
|
||||
@@ -1423,7 +1505,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void ControlCore::Close()
|
||||
void ControlCore::Close(const bool async)
|
||||
{
|
||||
if (!_IsClosing())
|
||||
{
|
||||
@@ -1433,12 +1515,21 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_connection.TerminalOutput(_connectionOutputEventToken);
|
||||
_connectionStateChangedRevoker.revoke();
|
||||
|
||||
// GH#1996 - Close the connection asynchronously on a background
|
||||
// thread.
|
||||
// Since TermControl::Close is only ever triggered by the UI, we
|
||||
// don't really care to wait for the connection to be completely
|
||||
// closed. We can just do it whenever.
|
||||
_asyncCloseConnection();
|
||||
if (async)
|
||||
{
|
||||
// GH#1996 - Close the connection asynchronously on a background
|
||||
// thread.
|
||||
// Since TermControl::Close is only ever triggered by the UI, we
|
||||
// don't really care to wait for the connection to be completely
|
||||
// closed. We can just do it whenever.
|
||||
_asyncCloseConnection();
|
||||
}
|
||||
else
|
||||
{
|
||||
// see notes in ContentProcess::final_release for why there's
|
||||
// _also_ a synchronous version of this.
|
||||
_connection.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1589,8 +1680,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_terminal->MultiClickSelection(terminalPosition, mode);
|
||||
selectionNeedsToBeCopied = true;
|
||||
}
|
||||
_updateSelectionUI();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Updates the renderer's representation of the selection as well as the selection marker overlay in TermControl
|
||||
void ControlCore::_updateSelectionUI()
|
||||
{
|
||||
_renderer->TriggerSelection();
|
||||
// only show the markers if we're doing a keyboard selection or in mark mode
|
||||
const bool showMarkers{ _terminal->SelectionMode() >= ::Microsoft::Terminal::Core::Terminal::SelectionInteractionMode::Keyboard };
|
||||
_UpdateSelectionMarkersHandlers(*this, winrt::make<implementation::UpdateSelectionMarkersEventArgs>(!showMarkers));
|
||||
}
|
||||
|
||||
void ControlCore::AttachUiaEngine(::Microsoft::Console::Render::IRenderEngine* const pEngine)
|
||||
@@ -1859,13 +1959,24 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// - This is related to work done for GH#2988.
|
||||
void ControlCore::GotFocus()
|
||||
{
|
||||
_terminal->FocusChanged(true);
|
||||
_focusChanged(true);
|
||||
}
|
||||
|
||||
// See GotFocus.
|
||||
void ControlCore::LostFocus()
|
||||
{
|
||||
_terminal->FocusChanged(false);
|
||||
_focusChanged(false);
|
||||
}
|
||||
|
||||
void ControlCore::_focusChanged(bool focused)
|
||||
{
|
||||
// GH#13461 - temporarily turn off read-only mode, send the focus event,
|
||||
// then turn it back on. Even in focus mode, focus events are fine to
|
||||
// send. We don't want to pop a warning every time the control is
|
||||
// focused.
|
||||
const auto previous = std::exchange(_isReadOnly, false);
|
||||
const auto restore = wil::scope_exit([&]() { _isReadOnly = previous; });
|
||||
_terminal->FocusChanged(focused);
|
||||
}
|
||||
|
||||
bool ControlCore::_isBackgroundTransparent()
|
||||
@@ -1881,6 +1992,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
return Opacity() < 1.0f || UseAcrylic() || !_settings->BackgroundImage().empty() || _settings->UseBackgroundImageForWindow();
|
||||
}
|
||||
|
||||
double ControlCore::DisplayScale() const
|
||||
{
|
||||
return _compositionScale;
|
||||
}
|
||||
|
||||
uint64_t ControlCore::OwningHwnd()
|
||||
{
|
||||
return _owningHwnd;
|
||||
@@ -1890,6 +2006,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
if (owner != _owningHwnd && _connection)
|
||||
{
|
||||
// TODO GH#1256 change the midi HWND too
|
||||
if (auto conpty{ _connection.try_as<TerminalConnection::ConptyConnection>() })
|
||||
{
|
||||
conpty.ReparentWindow(owner);
|
||||
@@ -2017,19 +2134,27 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
}
|
||||
|
||||
const auto viewHeight = ViewHeight();
|
||||
const auto bufferSize = BufferHeight();
|
||||
|
||||
// UserScrollViewport, to update the Terminal about where the viewport should be
|
||||
// then raise a _terminalScrollPositionChanged to inform the control to update the scrollbar.
|
||||
if (tgt.has_value())
|
||||
{
|
||||
UserScrollViewport(tgt->start.y);
|
||||
_terminalScrollPositionChanged(tgt->start.y, viewHeight, bufferSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (direction == ScrollToMarkDirection::Last || direction == ScrollToMarkDirection::Next)
|
||||
{
|
||||
UserScrollViewport(BufferHeight());
|
||||
_terminalScrollPositionChanged(BufferHeight(), viewHeight, bufferSize);
|
||||
}
|
||||
else if (direction == ScrollToMarkDirection::First || direction == ScrollToMarkDirection::Previous)
|
||||
{
|
||||
UserScrollViewport(0);
|
||||
_terminalScrollPositionChanged(0, viewHeight, bufferSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
void SizeChanged(const double width, const double height);
|
||||
void ScaleChanged(const double scale);
|
||||
double DisplayScale() const;
|
||||
uint64_t SwapChainHandle() const;
|
||||
|
||||
void AdjustFontSize(int fontSizeDelta);
|
||||
@@ -82,9 +83,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void PasteText(const winrt::hstring& hstr);
|
||||
bool CopySelectionToClipboard(bool singleLine, const Windows::Foundation::IReference<CopyFormat>& formats);
|
||||
void SelectAll();
|
||||
void ClearSelection();
|
||||
bool ToggleBlockSelection();
|
||||
void ToggleMarkMode();
|
||||
bool IsInMarkMode() const;
|
||||
Control::SelectionInteractionMode SelectionMode() const;
|
||||
bool SwitchSelectionEndpoint();
|
||||
|
||||
void GotFocus();
|
||||
void LostFocus();
|
||||
@@ -102,7 +105,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
::Microsoft::Console::Types::IUiaData* GetUiaData() const;
|
||||
|
||||
void Close();
|
||||
void Close(const bool async = true);
|
||||
|
||||
#pragma region ICoreState
|
||||
const size_t TaskbarState() const noexcept;
|
||||
@@ -159,6 +162,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
bool HasSelection() const;
|
||||
bool CopyOnSelect() const;
|
||||
Windows::Foundation::Collections::IVector<winrt::hstring> SelectedText(bool trimTrailingWhitespace) const;
|
||||
Control::SelectionData SelectionInfo() const;
|
||||
void SetSelectionAnchor(const til::point position);
|
||||
void SetEndSelectionPoint(const til::point position);
|
||||
|
||||
@@ -189,6 +193,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
uint64_t OwningHwnd();
|
||||
void OwningHwnd(uint64_t owner);
|
||||
|
||||
winrt::Windows::System::DispatcherQueue Dispatcher();
|
||||
|
||||
RUNTIME_SETTING(double, Opacity, _settings->Opacity());
|
||||
RUNTIME_SETTING(bool, UseAcrylic, _settings->UseAcrylic());
|
||||
|
||||
@@ -214,6 +220,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
TYPED_EVENT(ReceivedOutput, IInspectable, IInspectable);
|
||||
TYPED_EVENT(FoundMatch, IInspectable, Control::FoundResultsArgs);
|
||||
TYPED_EVENT(ShowWindowChanged, IInspectable, Control::ShowWindowArgs);
|
||||
TYPED_EVENT(UpdateSelectionMarkers, IInspectable, Control::UpdateSelectionMarkersEventArgs);
|
||||
TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs);
|
||||
// clang-format on
|
||||
|
||||
private:
|
||||
@@ -269,6 +277,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
bool _setFontSizeUnderLock(int fontSize);
|
||||
void _updateFont(const bool initialUpdate = false);
|
||||
void _refreshSizeUnderLock();
|
||||
void _updateSelectionUI();
|
||||
|
||||
void _sendInputToConnection(std::wstring_view wstr);
|
||||
|
||||
@@ -306,10 +315,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void _setOpacity(const double opacity);
|
||||
|
||||
bool _isBackgroundTransparent();
|
||||
void _focusChanged(bool focused);
|
||||
|
||||
inline bool _IsClosing() const noexcept
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
// This may not be strictly true if the core is running out of proc
|
||||
// with XAML. This assert was hit frequently during initial x-proc
|
||||
// development, but seemingly not anymore. Keep an eye on it.
|
||||
if (_dispatcher)
|
||||
{
|
||||
// _closing isn't atomic and may only be accessed from the main thread.
|
||||
|
||||
@@ -29,6 +29,30 @@ namespace Microsoft.Terminal.Control
|
||||
All
|
||||
};
|
||||
|
||||
enum SelectionInteractionMode
|
||||
{
|
||||
None,
|
||||
Mouse,
|
||||
Keyboard,
|
||||
Mark
|
||||
};
|
||||
|
||||
[flags]
|
||||
enum SelectionEndpointTarget
|
||||
{
|
||||
Start = 0x1,
|
||||
End = 0x2
|
||||
};
|
||||
|
||||
struct SelectionData
|
||||
{
|
||||
Microsoft.Terminal.Core.Point StartPos;
|
||||
Microsoft.Terminal.Core.Point EndPos;
|
||||
SelectionEndpointTarget Endpoint;
|
||||
Boolean StartAtLeftBoundary;
|
||||
Boolean EndAtRightBoundary;
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass ControlCore : ICoreState
|
||||
{
|
||||
ControlCore(IControlSettings settings,
|
||||
@@ -48,6 +72,7 @@ namespace Microsoft.Terminal.Control
|
||||
Boolean HasUnfocusedAppearance();
|
||||
|
||||
UInt64 SwapChainHandle { get; };
|
||||
Double DisplayScale { get; };
|
||||
|
||||
Windows.Foundation.Size FontSize { get; };
|
||||
String FontFaceName { get; };
|
||||
@@ -65,10 +90,11 @@ namespace Microsoft.Terminal.Control
|
||||
void SendInput(String text);
|
||||
void PasteText(String text);
|
||||
void SelectAll();
|
||||
void ClearSelection();
|
||||
Boolean ToggleBlockSelection();
|
||||
void ToggleMarkMode();
|
||||
Boolean SwitchSelectionEndpoint();
|
||||
void ClearBuffer(ClearBufferType clearType);
|
||||
Boolean IsInMarkMode();
|
||||
|
||||
void SetHoveredCell(Microsoft.Terminal.Core.Point terminalPosition);
|
||||
void ClearHoveredCell();
|
||||
@@ -90,6 +116,8 @@ namespace Microsoft.Terminal.Control
|
||||
|
||||
Boolean HasSelection { get; };
|
||||
IVector<String> SelectedText(Boolean trimTrailingWhitespace);
|
||||
SelectionData SelectionInfo { get; };
|
||||
SelectionInteractionMode SelectionMode();
|
||||
|
||||
String HoveredUriText { get; };
|
||||
Windows.Foundation.IReference<Microsoft.Terminal.Core.Point> HoveredCell { get; };
|
||||
@@ -125,6 +153,7 @@ namespace Microsoft.Terminal.Control
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> ReceivedOutput;
|
||||
event Windows.Foundation.TypedEventHandler<Object, FoundResultsArgs> FoundMatch;
|
||||
event Windows.Foundation.TypedEventHandler<Object, ShowWindowArgs> ShowWindowChanged;
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, UpdateSelectionMarkersEventArgs> UpdateSelectionMarkers;
|
||||
event Windows.Foundation.TypedEventHandler<Object, OpenHyperlinkEventArgs> OpenHyperlink;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -142,7 +142,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// Method Description:
|
||||
// - Given a copy-able selection, get the selected text from the buffer and send it to the
|
||||
// Windows Clipboard (CascadiaWin32:main.cpp).
|
||||
// - CopyOnSelect does NOT clear the selection
|
||||
// Arguments:
|
||||
// - singleLine: collapse all of the text to one line
|
||||
// - formats: which formats to copy (defined by action's CopyFormatting arg). nullptr
|
||||
@@ -257,15 +256,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
else if (WI_IsFlagSet(buttonState, MouseButtonState::IsRightButtonDown))
|
||||
{
|
||||
// CopyOnSelect right click always pastes
|
||||
if (_core->CopyOnSelect() || !_core->HasSelection())
|
||||
// Try to copy the text and clear the selection
|
||||
const auto successfulCopy = CopySelectionToClipboard(shiftEnabled, nullptr);
|
||||
_core->ClearSelection();
|
||||
if (_core->CopyOnSelect() || !successfulCopy)
|
||||
{
|
||||
// CopyOnSelect: right click always pastes!
|
||||
// Otherwise: no selection --> paste
|
||||
RequestPasteTextFromClipboard();
|
||||
}
|
||||
else
|
||||
{
|
||||
CopySelectionToClipboard(shiftEnabled, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -383,6 +382,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
isLeftMouseRelease &&
|
||||
_selectionNeedsToBeCopied)
|
||||
{
|
||||
// IMPORTANT!
|
||||
// DO NOT clear the selection here!
|
||||
// Otherwise, the selection will be cleared immediately after you make it.
|
||||
CopySelectionToClipboard(false, nullptr);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,3 +13,4 @@
|
||||
#include "TransparencyChangedEventArgs.g.cpp"
|
||||
#include "FoundResultsArgs.g.cpp"
|
||||
#include "ShowWindowArgs.g.cpp"
|
||||
#include "UpdateSelectionMarkersEventArgs.g.cpp"
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "TransparencyChangedEventArgs.g.h"
|
||||
#include "FoundResultsArgs.g.h"
|
||||
#include "ShowWindowArgs.g.h"
|
||||
#include "UpdateSelectionMarkersEventArgs.g.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
@@ -157,4 +158,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
WINRT_PROPERTY(bool, ShowOrHide);
|
||||
};
|
||||
|
||||
struct UpdateSelectionMarkersEventArgs : public UpdateSelectionMarkersEventArgsT<UpdateSelectionMarkersEventArgs>
|
||||
{
|
||||
public:
|
||||
UpdateSelectionMarkersEventArgs(const bool clearMarkers) :
|
||||
_ClearMarkers(clearMarkers)
|
||||
{
|
||||
}
|
||||
|
||||
WINRT_PROPERTY(bool, ClearMarkers, false);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -69,7 +69,6 @@ namespace Microsoft.Terminal.Control
|
||||
Double Opacity { get; };
|
||||
}
|
||||
|
||||
|
||||
runtimeclass FoundResultsArgs
|
||||
{
|
||||
Boolean FoundMatch { get; };
|
||||
@@ -79,4 +78,9 @@ namespace Microsoft.Terminal.Control
|
||||
{
|
||||
Boolean ShowOrHide { get; };
|
||||
}
|
||||
|
||||
runtimeclass UpdateSelectionMarkersEventArgs
|
||||
{
|
||||
Boolean ClearMarkers { get; };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
_controlPadding = til::rect{ til::math::rounding, padding };
|
||||
}
|
||||
void InteractivityAutomationPeer::ParentProvider(AutomationPeer parentProvider)
|
||||
void InteractivityAutomationPeer::ParentProvider(const Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple& parentProvider)
|
||||
{
|
||||
_parentProvider = parentProvider;
|
||||
}
|
||||
@@ -171,7 +171,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
double InteractivityAutomationPeer::GetScaleFactor() const noexcept
|
||||
{
|
||||
return DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel();
|
||||
// If we were being used in a process that had a CoreWindow, we could just call
|
||||
// DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel();
|
||||
//
|
||||
// We won't always be though, so instead, ask our model what the DPI is.
|
||||
return _interactivity->Core().DisplayScale();
|
||||
}
|
||||
|
||||
void InteractivityAutomationPeer::ChangeViewport(const til::inclusive_rect& NewWindow)
|
||||
@@ -182,15 +186,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
XamlAutomation::ITextRangeProvider InteractivityAutomationPeer::_CreateXamlUiaTextRange(UIA::ITextRangeProvider* returnVal) const
|
||||
{
|
||||
// LOAD-BEARING: use _parentProvider->ProviderFromPeer(_parentProvider) instead of this->ProviderFromPeer(*this).
|
||||
// Since we split the automation peer into TermControlAutomationPeer and InteractivityAutomationPeer,
|
||||
// using "this" returns null. This can cause issues with some UIA Client scenarios like any navigation in Narrator.
|
||||
const auto parent{ _parentProvider.get() };
|
||||
if (!parent)
|
||||
if (!_parentProvider)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
const auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, parent.ProviderFromPeer(parent));
|
||||
|
||||
const auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, _parentProvider);
|
||||
return xutr.as<XamlAutomation::ITextRangeProvider>();
|
||||
};
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
void SetControlBounds(const Windows::Foundation::Rect bounds);
|
||||
void SetControlPadding(const Core::Padding padding);
|
||||
void ParentProvider(Windows::UI::Xaml::Automation::Peers::AutomationPeer parentProvider);
|
||||
void ParentProvider(const Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple& parentProvider);
|
||||
|
||||
#pragma region IUiaEventDispatcher
|
||||
void SignalSelectionChanged() override;
|
||||
@@ -81,7 +81,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
::Microsoft::WRL::ComPtr<::Microsoft::Terminal::TermControlUiaProvider> _uiaProvider;
|
||||
winrt::Microsoft::Terminal::Control::implementation::ControlInteractivity* _interactivity;
|
||||
weak_ref<Windows::UI::Xaml::Automation::Peers::AutomationPeer> _parentProvider;
|
||||
winrt::Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple _parentProvider{ nullptr };
|
||||
|
||||
til::rect _controlBounds{};
|
||||
til::rect _controlPadding{};
|
||||
|
||||
@@ -3,13 +3,11 @@
|
||||
namespace Microsoft.Terminal.Control
|
||||
{
|
||||
[default_interface] runtimeclass InteractivityAutomationPeer :
|
||||
Windows.UI.Xaml.Automation.Peers.AutomationPeer,
|
||||
Windows.UI.Xaml.Automation.Provider.ITextProvider
|
||||
{
|
||||
|
||||
void SetControlBounds(Windows.Foundation.Rect bounds);
|
||||
void SetControlPadding(Microsoft.Terminal.Core.Padding padding);
|
||||
void ParentProvider(Windows.UI.Xaml.Automation.Peers.AutomationPeer parentProvider);
|
||||
void ParentProvider(Windows.UI.Xaml.Automation.Provider.IRawElementProviderSimple provider);
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> SelectionChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> TextChanged;
|
||||
|
||||
@@ -208,4 +208,10 @@ Please either install the missing font or choose another one.</value>
|
||||
<value>No results found</value>
|
||||
<comment>Announced to a screen reader when the user searches for some text and there are no matches for that text in the terminal.</comment>
|
||||
</data>
|
||||
<data name="TermControl_ContentDiedTextBlock.Text" xml:space="preserve">
|
||||
<value>The content of this terminal was closed unexpectedly.</value>
|
||||
</data>
|
||||
<data name="TermControl_ContentDiedButton.Content" xml:space="preserve">
|
||||
<value>Close</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
//
|
||||
// The functions for handling content processes in the TermControl are largely
|
||||
// in TermControlContentManagement.cpp
|
||||
|
||||
#include "pch.h"
|
||||
#include "TermControl.h"
|
||||
@@ -31,8 +34,10 @@ using namespace winrt::Windows::ApplicationModel::DataTransfer;
|
||||
// The updates are throttled to limit power usage.
|
||||
constexpr const auto ScrollBarUpdateInterval = std::chrono::milliseconds(8);
|
||||
|
||||
// The minimum delay between updating the TSF input control.
|
||||
// This is already throttled primarily in the ControlCore, with a timeout of 100ms. We're adding another smaller one here, as the (potentially x-proc) call will come in off the UI thread
|
||||
// The minimum delay between updating the TSF input control. This is already
|
||||
// throttled primarily in the ControlCore, with a timeout of 100ms. We're adding
|
||||
// another smaller one here, as the (potentially x-proc) call will come in off
|
||||
// the UI thread
|
||||
constexpr const auto TsfRedrawInterval = std::chrono::milliseconds(8);
|
||||
|
||||
// The minimum delay between updating the locations of regex patterns
|
||||
@@ -50,6 +55,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
TermControl::TermControl(IControlSettings settings,
|
||||
Control::IControlAppearance unfocusedAppearance,
|
||||
TerminalConnection::ITerminalConnection connection) :
|
||||
TermControl(winrt::guid{}, settings, unfocusedAppearance, connection) {}
|
||||
|
||||
TermControl::TermControl(winrt::guid contentGuid,
|
||||
IControlSettings settings,
|
||||
Control::IControlAppearance unfocusedAppearance,
|
||||
TerminalConnection::ITerminalConnection connection) :
|
||||
_initializedTerminal{ false },
|
||||
_closing{ false },
|
||||
_isInternalScrollBarUpdate{ false },
|
||||
_autoScrollVelocity{ 0 },
|
||||
_autoScrollingPointerPoint{ std::nullopt },
|
||||
@@ -61,30 +74,52 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_interactivity = winrt::make<implementation::ControlInteractivity>(settings, unfocusedAppearance, connection);
|
||||
if (contentGuid != winrt::guid{})
|
||||
{
|
||||
_contentProc = create_instance<Control::ContentProcess>(contentGuid, CLSCTX_LOCAL_SERVER);
|
||||
if (_contentProc != nullptr)
|
||||
{
|
||||
// TODO! think harder about ordering of Attach here.
|
||||
_contentProc.Attach();
|
||||
|
||||
_interactivity = _contentProc.GetInteractivity();
|
||||
_contentWaitInterrupt.create();
|
||||
_createContentWaitThread();
|
||||
}
|
||||
}
|
||||
|
||||
if (_interactivity == nullptr)
|
||||
{
|
||||
_interactivity = winrt::make<implementation::ControlInteractivity>(settings, unfocusedAppearance, connection);
|
||||
}
|
||||
|
||||
_core = _interactivity.Core();
|
||||
|
||||
// These events might all be triggered by the connection, but that
|
||||
// should be drained and closed before we complete destruction. So these
|
||||
// are safe.
|
||||
_core.ScrollPositionChanged({ this, &TermControl::_ScrollPositionChanged });
|
||||
_core.WarningBell({ this, &TermControl::_coreWarningBell });
|
||||
_core.CursorPositionChanged({ this, &TermControl::_CursorPositionChanged });
|
||||
_core.ScrollPositionChanged({ get_weak(), &TermControl::_ScrollPositionChanged });
|
||||
_core.WarningBell({ get_weak(), &TermControl::_coreWarningBell });
|
||||
_core.CursorPositionChanged({ get_weak(), &TermControl::_CursorPositionChanged });
|
||||
|
||||
// This event is specifically triggered by the renderer thread, a BG thread. Use a weak ref here.
|
||||
_core.RendererEnteredErrorState({ get_weak(), &TermControl::_RendererEnteredErrorState });
|
||||
|
||||
_core.ConnectionStateChanged({ get_weak(), &TermControl::_coreConnectionStateChanged });
|
||||
|
||||
// These callbacks can only really be triggered by UI interactions. So
|
||||
// they don't need weak refs - they can't be triggered unless we're
|
||||
// alive.
|
||||
_core.BackgroundColorChanged({ this, &TermControl::_coreBackgroundColorChanged });
|
||||
_core.FontSizeChanged({ this, &TermControl::_coreFontSizeChanged });
|
||||
_core.TransparencyChanged({ this, &TermControl::_coreTransparencyChanged });
|
||||
_core.RaiseNotice({ this, &TermControl::_coreRaisedNotice });
|
||||
_core.HoveredHyperlinkChanged({ this, &TermControl::_hoveredHyperlinkChanged });
|
||||
_core.FoundMatch({ this, &TermControl::_coreFoundMatch });
|
||||
_interactivity.OpenHyperlink({ this, &TermControl::_HyperlinkHandler });
|
||||
_interactivity.ScrollPositionChanged({ this, &TermControl::_ScrollPositionChanged });
|
||||
_core.BackgroundColorChanged({ get_weak(), &TermControl::_coreBackgroundColorChanged });
|
||||
_core.FontSizeChanged({ get_weak(), &TermControl::_coreFontSizeChanged });
|
||||
_core.TransparencyChanged({ get_weak(), &TermControl::_coreTransparencyChanged });
|
||||
_core.RaiseNotice({ get_weak(), &TermControl::_coreRaisedNotice });
|
||||
_core.HoveredHyperlinkChanged({ get_weak(), &TermControl::_hoveredHyperlinkChanged });
|
||||
_core.FoundMatch({ get_weak(), &TermControl::_coreFoundMatch });
|
||||
_core.UpdateSelectionMarkers({ get_weak(), &TermControl::_updateSelectionMarkers });
|
||||
_core.OpenHyperlink({ get_weak(), &TermControl::_HyperlinkHandler });
|
||||
_interactivity.OpenHyperlink({ get_weak(), &TermControl::_HyperlinkHandler });
|
||||
_interactivity.ScrollPositionChanged({ get_weak(), &TermControl::_ScrollPositionChanged });
|
||||
|
||||
// Initialize the terminal only once the swapchainpanel is loaded - that
|
||||
// way, we'll be able to query the real pixel size it got on layout
|
||||
@@ -135,6 +170,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_ApplyUISettings();
|
||||
}
|
||||
|
||||
Control::ContentProcess TermControl::ContentProc() const noexcept
|
||||
{
|
||||
return _contentProc;
|
||||
}
|
||||
|
||||
void TermControl::_throttledUpdateScrollbar(const ScrollBarUpdate& update)
|
||||
{
|
||||
// Assumptions:
|
||||
@@ -358,6 +398,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// switch from a solid color brush to an acrylic one.
|
||||
_changeBackgroundColor(bg);
|
||||
|
||||
// Update selection markers
|
||||
Windows::UI::Xaml::Media::SolidColorBrush cursorColorBrush{ til::color{ newAppearance.CursorColor() } };
|
||||
SelectionStartMarker().Fill(cursorColorBrush);
|
||||
SelectionEndMarker().Fill(cursorColorBrush);
|
||||
|
||||
// Set TSF Foreground
|
||||
Media::SolidColorBrush foregroundBrush{};
|
||||
if (_core.Settings().UseBackgroundImageForWindow())
|
||||
@@ -426,12 +471,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// achieve the intended effect.
|
||||
ScrollBar().IndicatorMode(Controls::Primitives::ScrollingIndicatorMode::None);
|
||||
ScrollBar().Visibility(Visibility::Collapsed);
|
||||
ScrollMarksGrid().Visibility(Visibility::Collapsed);
|
||||
}
|
||||
else // (default or Visible)
|
||||
{
|
||||
// Default behavior
|
||||
ScrollBar().IndicatorMode(Controls::Primitives::ScrollingIndicatorMode::MouseIndicator);
|
||||
ScrollBar().Visibility(Visibility::Visible);
|
||||
ScrollMarksGrid().Visibility(Visibility::Visible);
|
||||
}
|
||||
|
||||
_interactivity.UpdateSettings();
|
||||
@@ -562,6 +609,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
RootGrid().Background(solidColor);
|
||||
}
|
||||
|
||||
BackgroundBrush(RootGrid().Background());
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -607,6 +656,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
solidColor.Color(bg);
|
||||
}
|
||||
|
||||
BackgroundBrush(RootGrid().Background());
|
||||
|
||||
// Don't use the normal BackgroundBrush() Observable Property setter
|
||||
// here. (e.g. `BackgroundBrush()`). The one from the macro will
|
||||
// automatically ignore changes where the value doesn't _actually_
|
||||
// change. In our case, most of the time when changing the colors of the
|
||||
// background, the _Brush_ itself doesn't change, we simply change the
|
||||
// Color() of the brush. This results in the event not getting bubbled
|
||||
// up.
|
||||
//
|
||||
// Firing it manually makes sure it does.
|
||||
_BackgroundBrush = RootGrid().Background();
|
||||
_PropertyChangedHandlers(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"BackgroundBrush" });
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -646,6 +709,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
TermControl::~TermControl()
|
||||
{
|
||||
if (_contentIsOutOfProc())
|
||||
{
|
||||
_contentWaitInterrupt.SetEvent();
|
||||
_contentWaitThread.join();
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
@@ -692,7 +760,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
TerminalConnection::ConnectionState TermControl::ConnectionState() const
|
||||
{
|
||||
return _core.ConnectionState();
|
||||
try
|
||||
{
|
||||
return _core.ConnectionState();
|
||||
}
|
||||
CATCH_LOG();
|
||||
return TerminalConnection::ConnectionState::Failed;
|
||||
}
|
||||
|
||||
winrt::fire_and_forget TermControl::RenderEngineSwapChainChanged(IInspectable /*sender*/, IInspectable /*args*/)
|
||||
@@ -706,8 +779,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
if (auto control{ weakThis.get() })
|
||||
{
|
||||
const auto chainHandle = reinterpret_cast<HANDLE>(control->_core.SwapChainHandle());
|
||||
_AttachDxgiSwapChainToXaml(chainHandle);
|
||||
control->_acquireAndAttachSwapChainHandle();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -759,6 +831,28 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
nativePanel->SetSwapChainHandle(swapChainHandle);
|
||||
}
|
||||
|
||||
void TermControl::_acquireAndAttachSwapChainHandle()
|
||||
{
|
||||
const auto chainHandle = reinterpret_cast<HANDLE>(_contentIsOutOfProc() ?
|
||||
_contentProc.RequestSwapChainHandle(GetCurrentProcessId()) :
|
||||
_core.SwapChainHandle());
|
||||
// If we're in-proc, then the render engine has it's own ownership
|
||||
// handle to the swapchain. We don't need to also own it in the XAML
|
||||
// layer. It doesn't really make sense for us to duplicate it to
|
||||
// ourself again, so we're just gonna not.
|
||||
//
|
||||
// If we're out of proc though, the content proc has one handle in
|
||||
// it's process, and now there's a HANDLE in our process with this
|
||||
// value. Wrap that boy up in a unique_handle so that we can release
|
||||
// our handle when needed.
|
||||
if (_contentIsOutOfProc())
|
||||
{
|
||||
_contentSwapChain.reset(chainHandle);
|
||||
}
|
||||
|
||||
_AttachDxgiSwapChainToXaml(chainHandle);
|
||||
}
|
||||
|
||||
bool TermControl::_InitializeTerminal()
|
||||
{
|
||||
if (_initializedTerminal)
|
||||
@@ -788,13 +882,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
const auto coreInitialized = _core.Initialize(panelWidth,
|
||||
panelHeight,
|
||||
panelScaleX);
|
||||
if (!coreInitialized)
|
||||
// ControlCore::Initialize will return false if it was already
|
||||
// initialized. In that case, don't init the rest of the interactivity,
|
||||
// but do go on and hook us up to the core's swapchain and callbacks.
|
||||
if (coreInitialized)
|
||||
{
|
||||
return false;
|
||||
_interactivity.Initialize();
|
||||
}
|
||||
_interactivity.Initialize();
|
||||
|
||||
_AttachDxgiSwapChainToXaml(reinterpret_cast<HANDLE>(_core.SwapChainHandle()));
|
||||
_acquireAndAttachSwapChainHandle();
|
||||
|
||||
// Tell the DX Engine to notify us when the swap chain changes. We do
|
||||
// this after we initially set the swapchain so as to avoid unnecessary
|
||||
@@ -1182,7 +1278,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
// Manually show the cursor when a key is pressed. Restarting
|
||||
// the timer prevents flickering.
|
||||
_core.CursorOn(!_core.IsInMarkMode());
|
||||
_core.CursorOn(_core.SelectionMode() != SelectionInteractionMode::Mark);
|
||||
_cursorTimer->Start();
|
||||
}
|
||||
|
||||
@@ -1653,7 +1749,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
if (_cursorTimer)
|
||||
{
|
||||
// When the terminal focuses, show the cursor immediately
|
||||
_core.CursorOn(!_core.IsInMarkMode());
|
||||
_core.CursorOn(_core.SelectionMode() != SelectionInteractionMode::Mark);
|
||||
_cursorTimer->Start();
|
||||
}
|
||||
|
||||
@@ -1783,6 +1879,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
if (!_IsClosing())
|
||||
{
|
||||
// TODO:GH#5000 - move all the blinking inside of ControlCore.
|
||||
// There's no reason that the timer should live in TermControl just
|
||||
// to hop across the process boundary to toggle the content process.
|
||||
// That can all live in the control core.
|
||||
_core.BlinkCursor();
|
||||
}
|
||||
}
|
||||
@@ -1797,6 +1897,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
if (!_IsClosing())
|
||||
{
|
||||
// TODO:GH#5000 - move all the blinking inside of ControlCore.
|
||||
// There's no reason that the timer should live in TermControl just
|
||||
// to hop across the process boundary to toggle the content process.
|
||||
// That can all live in the control core.
|
||||
_core.BlinkAttributeTick();
|
||||
}
|
||||
}
|
||||
@@ -1831,6 +1935,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
update.newValue = args.ViewTop();
|
||||
|
||||
_updateScrollBar->Run(update);
|
||||
|
||||
// if a selection marker is already visible,
|
||||
// update the position of those markers
|
||||
if (SelectionStartMarker().Visibility() == Visibility::Visible || SelectionEndMarker().Visibility() == Visibility::Visible)
|
||||
{
|
||||
_updateSelectionMarkers(nullptr, winrt::make<UpdateSelectionMarkersEventArgs>(false));
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -1889,7 +2000,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
return false;
|
||||
}
|
||||
|
||||
return _interactivity.CopySelectionToClipboard(singleLine, formats);
|
||||
const auto successfulCopy = _interactivity.CopySelectionToClipboard(singleLine, formats);
|
||||
_core.ClearSelection();
|
||||
return successfulCopy;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -1914,6 +2027,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_core.ToggleMarkMode();
|
||||
}
|
||||
|
||||
bool TermControl::SwitchSelectionEndpoint()
|
||||
{
|
||||
return _core.SwitchSelectionEndpoint();
|
||||
}
|
||||
|
||||
void TermControl::Close()
|
||||
{
|
||||
if (!_IsClosing())
|
||||
@@ -1925,8 +2043,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// Disconnect the TSF input control so it doesn't receive EditContext events.
|
||||
TSFInputControl().Close();
|
||||
_autoScrollTimer.Stop();
|
||||
|
||||
_core.Close();
|
||||
// THIS IS IMPORTANT!
|
||||
//
|
||||
// If we're out of proc, the control can be closed, but we DON'T
|
||||
// want that to close our content. We'll want the content proc's
|
||||
// teardown to be responsible for closing the core.
|
||||
if (!_contentIsOutOfProc())
|
||||
{
|
||||
_core.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2735,8 +2860,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_core.ClearHoveredCell();
|
||||
}
|
||||
|
||||
winrt::fire_and_forget TermControl::_hoveredHyperlinkChanged(IInspectable sender,
|
||||
IInspectable args)
|
||||
winrt::fire_and_forget TermControl::_hoveredHyperlinkChanged(IInspectable /*sender*/,
|
||||
IInspectable /*args*/)
|
||||
{
|
||||
auto weakThis{ get_weak() };
|
||||
co_await wil::resume_foreground(Dispatcher());
|
||||
@@ -2763,12 +2888,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
HyperlinkTooltipBorder().BorderThickness(newThickness);
|
||||
|
||||
// Compute the location of the top left corner of the cell in DIPS
|
||||
const til::size marginsInDips{ til::math::rounding, GetPadding().Left, GetPadding().Top };
|
||||
const til::point startPos{ lastHoveredCell.Value() };
|
||||
const til::size fontSize{ til::math::rounding, _core.FontSize() };
|
||||
const auto posInPixels{ startPos * fontSize };
|
||||
const til::point posInDIPs{ til::math::flooring, posInPixels.x / scale, posInPixels.y / scale };
|
||||
const auto locationInDIPs{ posInDIPs + marginsInDips };
|
||||
const til::point locationInDIPs{ _toPosInDips(lastHoveredCell.Value()) };
|
||||
|
||||
// Move the border to the top left corner of the cell
|
||||
OverlayCanvas().SetLeft(HyperlinkTooltipBorder(), locationInDIPs.x - offset.x);
|
||||
@@ -2778,10 +2898,118 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
}
|
||||
|
||||
winrt::fire_and_forget TermControl::_updateSelectionMarkers(IInspectable /*sender*/, Control::UpdateSelectionMarkersEventArgs args)
|
||||
{
|
||||
auto weakThis{ get_weak() };
|
||||
co_await resume_foreground(Dispatcher());
|
||||
if (weakThis.get() && args)
|
||||
{
|
||||
if (_core.HasSelection() && !args.ClearMarkers())
|
||||
{
|
||||
// retrieve all of the necessary selection marker data
|
||||
// from the TerminalCore layer under one lock to improve performance
|
||||
const auto markerData{ _core.SelectionInfo() };
|
||||
|
||||
// lambda helper function that can be used to display a selection marker
|
||||
// - targetEnd: if true, target the "end" selection marker. Otherwise, target "start".
|
||||
auto displayMarker = [&](bool targetEnd) {
|
||||
const auto flipMarker{ targetEnd ? markerData.EndAtRightBoundary : markerData.StartAtLeftBoundary };
|
||||
const auto& marker{ targetEnd ? SelectionEndMarker() : SelectionStartMarker() };
|
||||
|
||||
// Ensure the marker is oriented properly
|
||||
// (i.e. if start is at the beginning of the buffer, it should be flipped)
|
||||
auto transform{ marker.RenderTransform().as<Windows::UI::Xaml::Media::ScaleTransform>() };
|
||||
transform.ScaleX(std::abs(transform.ScaleX()) * (flipMarker ? -1.0 : 1.0));
|
||||
marker.RenderTransform(transform);
|
||||
|
||||
// Compute the location of the top left corner of the cell in DIPS
|
||||
auto terminalPos{ targetEnd ? markerData.EndPos : markerData.StartPos };
|
||||
if (flipMarker)
|
||||
{
|
||||
// When we flip the marker, a negative scaling makes us be one cell-width to the left.
|
||||
// Add one to the viewport pos' x-coord to fix that.
|
||||
terminalPos.X += 1;
|
||||
}
|
||||
const til::point locationInDIPs{ _toPosInDips(terminalPos) };
|
||||
|
||||
// Move the marker to the top left corner of the cell
|
||||
SelectionCanvas().SetLeft(marker,
|
||||
(locationInDIPs.x - SwapChainPanel().ActualOffset().x));
|
||||
SelectionCanvas().SetTop(marker,
|
||||
(locationInDIPs.y - SwapChainPanel().ActualOffset().y));
|
||||
marker.Visibility(Visibility::Visible);
|
||||
};
|
||||
|
||||
// show/update selection markers
|
||||
// figure out which endpoint to move, get it and the relevant icon (hide the other icon)
|
||||
const auto movingEnd{ WI_IsFlagSet(markerData.Endpoint, SelectionEndpointTarget::End) };
|
||||
const auto selectionAnchor{ movingEnd ? markerData.EndPos : markerData.StartPos };
|
||||
const auto& marker{ movingEnd ? SelectionEndMarker() : SelectionStartMarker() };
|
||||
const auto& otherMarker{ movingEnd ? SelectionStartMarker() : SelectionEndMarker() };
|
||||
if (selectionAnchor.Y < 0 || selectionAnchor.Y >= _core.ViewHeight())
|
||||
{
|
||||
// if the endpoint is outside of the viewport,
|
||||
// just hide the markers
|
||||
marker.Visibility(Visibility::Collapsed);
|
||||
otherMarker.Visibility(Visibility::Collapsed);
|
||||
co_return;
|
||||
}
|
||||
else if (WI_AreAllFlagsSet(markerData.Endpoint, SelectionEndpointTarget::Start | SelectionEndpointTarget::End))
|
||||
{
|
||||
// display both markers
|
||||
displayMarker(true);
|
||||
displayMarker(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// display one marker,
|
||||
// but hide the other
|
||||
displayMarker(movingEnd);
|
||||
otherMarker.Visibility(Visibility::Collapsed);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// hide selection markers
|
||||
SelectionStartMarker().Visibility(Visibility::Collapsed);
|
||||
SelectionEndMarker().Visibility(Visibility::Collapsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
til::point TermControl::_toPosInDips(const Core::Point terminalCellPos)
|
||||
{
|
||||
const til::point terminalPos{ terminalCellPos };
|
||||
const til::size marginsInDips{ til::math::rounding, GetPadding().Left, GetPadding().Top };
|
||||
const til::size fontSize{ til::math::rounding, _core.FontSize() };
|
||||
const til::point posInPixels{ terminalPos * fontSize };
|
||||
const auto scale{ SwapChainPanel().CompositionScaleX() };
|
||||
const til::point posInDIPs{ til::math::flooring, posInPixels.x / scale, posInPixels.y / scale };
|
||||
return posInDIPs + marginsInDips;
|
||||
}
|
||||
|
||||
void TermControl::_coreFontSizeChanged(const int fontWidth,
|
||||
const int fontHeight,
|
||||
const bool isInitialChange)
|
||||
{
|
||||
// scale the selection markers to be the size of a cell
|
||||
auto scaleMarker = [fontWidth, fontHeight, dpiScale{ SwapChainPanel().CompositionScaleX() }](const Windows::UI::Xaml::Shapes::Path& shape) {
|
||||
// The selection markers were designed to be 5x14 in size,
|
||||
// so use those dimensions below for the scaling
|
||||
const auto scaleX = fontWidth / 5.0 / dpiScale;
|
||||
const auto scaleY = fontHeight / 14.0 / dpiScale;
|
||||
|
||||
Windows::UI::Xaml::Media::ScaleTransform transform;
|
||||
transform.ScaleX(scaleX);
|
||||
transform.ScaleY(scaleY);
|
||||
shape.RenderTransform(transform);
|
||||
|
||||
// now hide the shape
|
||||
shape.Visibility(Visibility::Collapsed);
|
||||
};
|
||||
scaleMarker(SelectionStartMarker());
|
||||
scaleMarker(SelectionEndMarker());
|
||||
|
||||
// Don't try to inspect the core here. The Core is raising this while
|
||||
// it's holding its write lock. If the handlers calls back to some
|
||||
// method on the TermControl on the same thread, and that _method_ calls
|
||||
@@ -2908,6 +3136,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
return _core.OwningHwnd();
|
||||
}
|
||||
|
||||
void TermControl::_coreConnectionStateChanged(const IInspectable& /*sender*/, const IInspectable& /*args*/)
|
||||
{
|
||||
_ConnectionStateChangedHandlers(*this, nullptr);
|
||||
}
|
||||
|
||||
void TermControl::AddMark(const Control::ScrollMark& mark)
|
||||
{
|
||||
_core.AddMark(mark);
|
||||
|
||||
@@ -29,10 +29,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
Control::IControlAppearance unfocusedAppearance,
|
||||
TerminalConnection::ITerminalConnection connection);
|
||||
|
||||
TermControl(winrt::guid contentGuid,
|
||||
IControlSettings settings,
|
||||
Control::IControlAppearance unfocusedAppearance,
|
||||
TerminalConnection::ITerminalConnection connection);
|
||||
|
||||
winrt::fire_and_forget UpdateControlSettings(Control::IControlSettings settings);
|
||||
winrt::fire_and_forget UpdateControlSettings(Control::IControlSettings settings, Control::IControlAppearance unfocusedAppearance);
|
||||
IControlSettings Settings() const;
|
||||
|
||||
winrt::guid ContentGuid() const;
|
||||
|
||||
hstring GetProfileName() const;
|
||||
|
||||
bool CopySelectionToClipboard(bool singleLine, const Windows::Foundation::IReference<CopyFormat>& formats);
|
||||
@@ -40,6 +47,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void SelectAll();
|
||||
bool ToggleBlockSelection();
|
||||
void ToggleMarkMode();
|
||||
bool SwitchSelectionEndpoint();
|
||||
void Close();
|
||||
Windows::Foundation::Size CharacterDimensions() const;
|
||||
Windows::Foundation::Size MinimumSize();
|
||||
@@ -92,6 +100,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
winrt::fire_and_forget _RendererEnteredErrorState(IInspectable sender, IInspectable args);
|
||||
|
||||
void _RenderRetryButton_Click(const IInspectable& button, const IInspectable& args);
|
||||
void _ContentDiedCloseButton_Click(const IInspectable& button, const IInspectable& args);
|
||||
winrt::fire_and_forget _RendererWarning(IInspectable sender,
|
||||
Control::RendererWarningArgs args);
|
||||
|
||||
@@ -129,6 +138,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
void AdjustOpacity(const double opacity, const bool relative);
|
||||
|
||||
Control::ContentProcess ContentProc() const noexcept;
|
||||
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
|
||||
// -------------------------------- WinRT Events ---------------------------------
|
||||
// clang-format off
|
||||
WINRT_CALLBACK(FontSizeChanged, Control::FontSizeChangedEventArgs);
|
||||
@@ -137,9 +150,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
PROJECTED_FORWARDED_TYPED_EVENT(TitleChanged, IInspectable, Control::TitleChangedEventArgs, _core, TitleChanged);
|
||||
PROJECTED_FORWARDED_TYPED_EVENT(TabColorChanged, IInspectable, IInspectable, _core, TabColorChanged);
|
||||
PROJECTED_FORWARDED_TYPED_EVENT(SetTaskbarProgress, IInspectable, IInspectable, _core, TaskbarProgressChanged);
|
||||
PROJECTED_FORWARDED_TYPED_EVENT(ConnectionStateChanged, IInspectable, IInspectable, _core, ConnectionStateChanged);
|
||||
PROJECTED_FORWARDED_TYPED_EVENT(ShowWindowChanged, IInspectable, Control::ShowWindowArgs, _core, ShowWindowChanged);
|
||||
|
||||
TYPED_EVENT(ConnectionStateChanged, IInspectable, IInspectable);
|
||||
|
||||
PROJECTED_FORWARDED_TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs, _interactivity, PasteFromClipboard);
|
||||
|
||||
TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs);
|
||||
@@ -152,6 +166,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
TYPED_EVENT(WarningBell, IInspectable, IInspectable);
|
||||
// clang-format on
|
||||
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, BackgroundBrush, _PropertyChangedHandlers, nullptr);
|
||||
|
||||
private:
|
||||
friend struct TermControlT<TermControl>; // friend our parent so it can bind private event handlers
|
||||
|
||||
@@ -166,6 +182,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
Control::TermControlAutomationPeer _automationPeer{ nullptr };
|
||||
Control::ControlInteractivity _interactivity{ nullptr };
|
||||
Control::ControlCore _core{ nullptr };
|
||||
Control::ContentProcess _contentProc{ nullptr };
|
||||
|
||||
winrt::com_ptr<SearchBoxControl> _searchBox;
|
||||
|
||||
@@ -204,6 +221,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
winrt::Windows::UI::Xaml::Controls::SwapChainPanel::LayoutUpdated_revoker _layoutUpdatedRevoker;
|
||||
bool _showMarksInScrollbar{ false };
|
||||
|
||||
wil::unique_event _contentWaitInterrupt;
|
||||
std::thread _contentWaitThread;
|
||||
wil::unique_handle _contentSwapChain;
|
||||
void _createContentWaitThread();
|
||||
bool _contentIsOutOfProc() const;
|
||||
|
||||
void _acquireAndAttachSwapChainHandle();
|
||||
|
||||
inline bool _IsClosing() const noexcept
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
@@ -287,6 +312,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void _FontInfoHandler(const IInspectable& sender, const FontInfoEventArgs& eventArgs);
|
||||
|
||||
winrt::fire_and_forget _hoveredHyperlinkChanged(IInspectable sender, IInspectable args);
|
||||
winrt::fire_and_forget _updateSelectionMarkers(IInspectable sender, Control::UpdateSelectionMarkersEventArgs args);
|
||||
|
||||
void _coreFontSizeChanged(const int fontWidth,
|
||||
const int fontHeight,
|
||||
@@ -294,7 +320,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
winrt::fire_and_forget _coreTransparencyChanged(IInspectable sender, Control::TransparencyChangedEventArgs args);
|
||||
void _coreRaisedNotice(const IInspectable& s, const Control::NoticeEventArgs& args);
|
||||
void _coreWarningBell(const IInspectable& sender, const IInspectable& args);
|
||||
|
||||
void _coreFoundMatch(const IInspectable& sender, const Control::FoundResultsArgs& args);
|
||||
|
||||
winrt::fire_and_forget _raiseContentDied();
|
||||
void _coreConnectionStateChanged(const IInspectable& sender, const IInspectable& args);
|
||||
|
||||
til::point _toPosInDips(const Core::Point terminalCellPos);
|
||||
void _throttledUpdateScrollbar(const ScrollBarUpdate& update);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import "IDirectKeyListener.idl";
|
||||
import "EventArgs.idl";
|
||||
import "ICoreState.idl";
|
||||
import "ControlCore.idl";
|
||||
import "ContentProcess.idl";
|
||||
|
||||
namespace Microsoft.Terminal.Control
|
||||
{
|
||||
@@ -14,8 +15,14 @@ namespace Microsoft.Terminal.Control
|
||||
[default_interface] runtimeclass TermControl : Windows.UI.Xaml.Controls.UserControl,
|
||||
IDirectKeyListener,
|
||||
IMouseWheelListener,
|
||||
ICoreState
|
||||
ICoreState,
|
||||
Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
TermControl(Guid contentGuid,
|
||||
IControlSettings settings,
|
||||
IControlAppearance unfocusedAppearance,
|
||||
Microsoft.Terminal.TerminalConnection.ITerminalConnection connection);
|
||||
|
||||
TermControl(IControlSettings settings,
|
||||
IControlAppearance unfocusedAppearance,
|
||||
Microsoft.Terminal.TerminalConnection.ITerminalConnection connection);
|
||||
@@ -25,6 +32,9 @@ namespace Microsoft.Terminal.Control
|
||||
void UpdateControlSettings(IControlSettings settings);
|
||||
void UpdateControlSettings(IControlSettings settings, IControlAppearance unfocusedAppearance);
|
||||
|
||||
Guid ContentGuid{ get; };
|
||||
ContentProcess ContentProc{ get; };
|
||||
|
||||
Microsoft.Terminal.Control.IControlSettings Settings { get; };
|
||||
|
||||
event FontSizeChangedEventArgs FontSizeChanged;
|
||||
@@ -53,6 +63,7 @@ namespace Microsoft.Terminal.Control
|
||||
void SelectAll();
|
||||
Boolean ToggleBlockSelection();
|
||||
void ToggleMarkMode();
|
||||
Boolean SwitchSelectionEndpoint();
|
||||
void ClearBuffer(ClearBufferType clearType);
|
||||
void Close();
|
||||
Windows.Foundation.Size CharacterDimensions { get; };
|
||||
@@ -89,5 +100,6 @@ namespace Microsoft.Terminal.Control
|
||||
// opacity set by the settings should call this instead.
|
||||
Double BackgroundOpacity { get; };
|
||||
|
||||
Windows.UI.Xaml.Media.Brush BackgroundBrush { get; };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1213,6 +1213,16 @@
|
||||
</ToolTipService.ToolTip>
|
||||
</Border>
|
||||
</Canvas>
|
||||
|
||||
<Canvas x:Name="SelectionCanvas"
|
||||
Visibility="Visible">
|
||||
<Path Name="SelectionStartMarker"
|
||||
Data="M 0 0 L 4 5.5996094 L 4 14 L 5 14 L 5 7 L 5 4.1992188 L 5 0 L 0 0 z "
|
||||
Visibility="Collapsed" />
|
||||
<Path Name="SelectionEndMarker"
|
||||
Data="M 0 0 L 0 4.1992188 L 0 7 L 0 14 L 1 14 L 1 5.5996094 L 5 0 L 0 0 z "
|
||||
Visibility="Collapsed" />
|
||||
</Canvas>
|
||||
</SwapChainPanel>
|
||||
|
||||
<!--
|
||||
@@ -1295,6 +1305,27 @@
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
<Grid x:Name="ContentDiedNotice"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
x:Load="False">
|
||||
<Border Margin="8,8,8,8"
|
||||
Padding="8,8,8,8"
|
||||
Background="{ThemeResource SystemControlBackgroundAltHighBrush}"
|
||||
BorderBrush="{ThemeResource SystemAccentColor}"
|
||||
BorderThickness="2,2,2,2"
|
||||
CornerRadius="{ThemeResource OverlayCornerRadius}">
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="TermControl_ContentDiedTextBlock"
|
||||
HorizontalAlignment="Center"
|
||||
TextWrapping="WrapWholeWords" />
|
||||
<Button x:Uid="TermControl_ContentDiedButton"
|
||||
HorizontalAlignment="Right"
|
||||
Click="_ContentDiedCloseButton_Click" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
|
||||
</UserControl>
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include "pch.h"
|
||||
#include <UIAutomationCore.h>
|
||||
#include <LibraryResources.h>
|
||||
#include <ScopedResourceLoader.h>
|
||||
|
||||
#include "TermControlAutomationPeer.h"
|
||||
#include "TermControl.h"
|
||||
#include "TermControlAutomationPeer.g.cpp"
|
||||
@@ -83,7 +85,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_contentAutomationPeer.TextChanged([this](auto&&, auto&&) { SignalTextChanged(); });
|
||||
_contentAutomationPeer.CursorChanged([this](auto&&, auto&&) { SignalCursorChanged(); });
|
||||
_contentAutomationPeer.NewOutput([this](auto&&, hstring newOutput) { NotifyNewOutput(newOutput); });
|
||||
_contentAutomationPeer.ParentProvider(*this);
|
||||
_contentAutomationPeer.ParentProvider(GetParentProvider());
|
||||
};
|
||||
|
||||
// Method Description:
|
||||
@@ -117,6 +119,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
}
|
||||
|
||||
XamlAutomation::IRawElementProviderSimple TermControlAutomationPeer::GetParentProvider()
|
||||
{
|
||||
const auto parentProvider = this->ProviderFromPeer(*this);
|
||||
return parentProvider;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Signals the ui automation client that the terminal's selection has changed and should be updated
|
||||
// Arguments:
|
||||
@@ -261,7 +269,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
hstring TermControlAutomationPeer::GetLocalizedControlTypeCore() const
|
||||
{
|
||||
return RS_(L"TerminalControl_ControlType");
|
||||
// TerminalControl_ControlType is "terminal" in english. Usually, this
|
||||
// will return the localized version of that string. For applications
|
||||
// that use the TermControl that don't package up all the resources in a
|
||||
// way that they can be accessed, this will fall back to just "terminal"
|
||||
// (notably, the scratch solution doesn't package these resources)
|
||||
//
|
||||
// Stash this in a static variable in the off chance that this is called
|
||||
// in a hot path by some a11y tool. There's absolutely no chance it's
|
||||
// gonna change, so this is safe enough.
|
||||
static auto resMap{ ScopedResourceLoader(L"Microsoft.Terminal.Control/Resources").GetResourceMap() };
|
||||
return resMap ?
|
||||
RS_(L"TerminalControl_ControlType") :
|
||||
L"terminal";
|
||||
}
|
||||
|
||||
Windows::Foundation::IInspectable TermControlAutomationPeer::GetPatternCore(PatternInterface patternInterface) const
|
||||
|
||||
@@ -48,8 +48,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
void UpdateControlBounds();
|
||||
void SetControlPadding(const Core::Padding padding);
|
||||
|
||||
void RecordKeyEvent(const WORD vkey);
|
||||
|
||||
Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple GetParentProvider();
|
||||
|
||||
#pragma region FrameworkElementAutomationPeer
|
||||
hstring GetClassNameCore() const;
|
||||
Windows::UI::Xaml::Automation::Peers::AutomationControlType GetAutomationControlTypeCore() const;
|
||||
|
||||
@@ -12,5 +12,6 @@ namespace Microsoft.Terminal.Control
|
||||
|
||||
void UpdateControlBounds();
|
||||
void SetControlPadding(Microsoft.Terminal.Core.Padding padding);
|
||||
Windows.UI.Xaml.Automation.Provider.IRawElementProviderSimple GetParentProvider();
|
||||
}
|
||||
}
|
||||
|
||||
160
src/cascadia/TerminalControl/TermControlContentManagement.cpp
Normal file
160
src/cascadia/TerminalControl/TermControlContentManagement.cpp
Normal file
@@ -0,0 +1,160 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
//
|
||||
// The functions in this class are specific to the handling of out-of-proc
|
||||
// content processes by the TermControl. Putting them all in one file keeps
|
||||
// TermControl.cpp a little less cluttered.
|
||||
|
||||
#include "pch.h"
|
||||
#include "TermControl.h"
|
||||
|
||||
using namespace ::Microsoft::Console::Types;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Core;
|
||||
using namespace winrt::Windows::System;
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
winrt::guid TermControl::ContentGuid() const
|
||||
{
|
||||
return _contentIsOutOfProc() ? _contentProc.Guid() : winrt::guid{};
|
||||
}
|
||||
|
||||
bool TermControl::_contentIsOutOfProc() const
|
||||
{
|
||||
return _contentProc != nullptr;
|
||||
}
|
||||
|
||||
bool s_waitOnContentProcess(uint64_t contentPid, HANDLE contentWaitInterrupt)
|
||||
{
|
||||
// This is the array of HANDLEs that we're going to wait on in
|
||||
// WaitForMultipleObjects below.
|
||||
// * waits[0] will be the handle to the content process. It gets
|
||||
// signalled when the process exits / dies.
|
||||
// * waits[1] is the handle to our _contentWaitInterrupt event. Another
|
||||
// thread can use that to manually break this loop. We'll do that when
|
||||
// we're getting torn down.
|
||||
HANDLE waits[2];
|
||||
waits[1] = contentWaitInterrupt;
|
||||
bool displayError = true;
|
||||
|
||||
// At any point in all this, the content process might die. If it does,
|
||||
// we want to raise an error message, to inform that this control is now
|
||||
// dead.
|
||||
try
|
||||
{
|
||||
// This might fail to even ask the content for it's PID.
|
||||
wil::unique_handle hContent{ OpenProcess(SYNCHRONIZE,
|
||||
FALSE,
|
||||
static_cast<DWORD>(contentPid)) };
|
||||
|
||||
// If we fail to open the content, then they don't exist
|
||||
// anymore! We'll need to immediately raise the notification that the content has died.
|
||||
if (hContent.get() == nullptr)
|
||||
{
|
||||
const auto gle = GetLastError();
|
||||
TraceLoggingWrite(g_hTerminalControlProvider,
|
||||
"TermControl_FailedToOpenContent",
|
||||
TraceLoggingUInt64(gle, "lastError", "The result of GetLastError"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
return displayError;
|
||||
}
|
||||
|
||||
waits[0] = hContent.get();
|
||||
|
||||
switch (WaitForMultipleObjects(2, waits, FALSE, INFINITE))
|
||||
{
|
||||
case WAIT_OBJECT_0 + 0: // waits[0] was signaled, the handle to the content process
|
||||
|
||||
TraceLoggingWrite(g_hTerminalControlProvider,
|
||||
"TermControl_ContentDied",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
break;
|
||||
|
||||
case WAIT_OBJECT_0 + 1: // waits[1] was signaled, our manual interrupt
|
||||
|
||||
TraceLoggingWrite(g_hTerminalControlProvider,
|
||||
"TermControl_ContentWaitInterrupted",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
displayError = false;
|
||||
break;
|
||||
|
||||
case WAIT_TIMEOUT:
|
||||
// This should be impossible.
|
||||
TraceLoggingWrite(g_hTerminalControlProvider,
|
||||
"TermControl_ContentWaitTimeout",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
// Returning any other value is invalid. Just die.
|
||||
const auto gle = GetLastError();
|
||||
TraceLoggingWrite(g_hTerminalControlProvider,
|
||||
"TermControl_WaitFailed",
|
||||
TraceLoggingUInt64(gle, "lastError", "The result of GetLastError"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Theoretically, if window[1] dies when we're trying to get
|
||||
// its PID we'll get here. We can probably just exit here.
|
||||
|
||||
TraceLoggingWrite(g_hTerminalControlProvider,
|
||||
"TermControl_ExceptionInWaitThread",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
return displayError;
|
||||
}
|
||||
|
||||
void TermControl::_createContentWaitThread()
|
||||
{
|
||||
_contentWaitThread = std::thread([weakThis = get_weak(), contentPid = _contentProc.GetPID(), contentWaitInterrupt = _contentWaitInterrupt.get()] {
|
||||
if (s_waitOnContentProcess(contentPid, contentWaitInterrupt))
|
||||
{
|
||||
// When s_waitOnContentProcess returns, if it returned true, we
|
||||
// should display a dialog in our bounds to indicate that we
|
||||
// were closed unexpectedly. If we closed in an expected way,
|
||||
// then s_waitOnContentProcess will return false.
|
||||
if (auto control{ weakThis.get() })
|
||||
{
|
||||
control->_raiseContentDied();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
winrt::fire_and_forget TermControl::_raiseContentDied()
|
||||
{
|
||||
auto weakThis{ get_weak() };
|
||||
co_await winrt::resume_foreground(Dispatcher());
|
||||
|
||||
if (auto control{ weakThis.get() }; !control->_IsClosing())
|
||||
{
|
||||
if (auto loadedUiElement{ FindName(L"ContentDiedNotice") })
|
||||
{
|
||||
if (auto uiElement{ loadedUiElement.try_as<::winrt::Windows::UI::Xaml::UIElement>() })
|
||||
{
|
||||
uiElement.Visibility(Visibility::Visible);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Method Description:
|
||||
// - Handler for when the "Content Died" dialog's button is clicked.
|
||||
void TermControl::_ContentDiedCloseButton_Click(IInspectable const& /*sender*/, IInspectable const& /*args*/)
|
||||
{
|
||||
// Alert whoever's hosting us that the connection was closed.
|
||||
// When they come asking what the new connection state is, we'll reply with Closed
|
||||
_ConnectionStateChangedHandlers(*this, nullptr);
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,9 @@
|
||||
<!-- ========================= Headers ======================== -->
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="ContentProcess.h">
|
||||
<DependentUpon>ContentProcess.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ControlCore.h">
|
||||
<DependentUpon>ControlCore.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
@@ -64,13 +67,18 @@
|
||||
<ClInclude Include="TSFInputControl.h">
|
||||
<DependentUpon>TSFInputControl.xaml</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="XamlUiaTextRange.h" />
|
||||
<ClInclude Include="XamlUiaTextRange.h" >
|
||||
<DependentUpon>XamlUiaTextRange.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<!-- ========================= Cpp Files ======================== -->
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ContentProcess.cpp">
|
||||
<DependentUpon>ContentProcess.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ControlCore.cpp">
|
||||
<DependentUpon>ControlCore.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
@@ -93,6 +101,9 @@
|
||||
<ClCompile Include="TermControl.cpp">
|
||||
<DependentUpon>TermControl.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TermControlContentManagement.cpp">
|
||||
<DependentUpon>TermControl.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TSFInputControl.cpp">
|
||||
<DependentUpon>TSFInputControl.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
@@ -103,10 +114,13 @@
|
||||
<ClCompile Include="InteractivityAutomationPeer.cpp">
|
||||
<DependentUpon>InteractivityAutomationPeer.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="XamlUiaTextRange.cpp" />
|
||||
<ClCompile Include="XamlUiaTextRange.cpp" >
|
||||
<DependentUpon>XamlUiaTextRange.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<!-- ========================= idl Files ======================== -->
|
||||
<ItemGroup>
|
||||
<Midl Include="ContentProcess.idl" />
|
||||
<Midl Include="ControlCore.idl" />
|
||||
<Midl Include="ControlInteractivity.idl" />
|
||||
<Midl Include="ICoreState.idl" />
|
||||
@@ -129,6 +143,7 @@
|
||||
<Midl Include="TSFInputControl.idl">
|
||||
<DependentUpon>TSFInputControl.xaml</DependentUpon>
|
||||
</Midl>
|
||||
<Midl Include="XamlUiaTextRange.idl" />
|
||||
</ItemGroup>
|
||||
<!-- ========================= XAML Files ======================== -->
|
||||
<ItemGroup>
|
||||
|
||||
@@ -21,13 +21,13 @@ Author(s):
|
||||
#pragma once
|
||||
|
||||
#include "TermControlAutomationPeer.h"
|
||||
#include "XamlUiaTextRange.g.h"
|
||||
#include <UIAutomationCore.h>
|
||||
#include "../types/TermControlUiaTextRange.hpp"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
class XamlUiaTextRange :
|
||||
public winrt::implements<XamlUiaTextRange, Windows::UI::Xaml::Automation::Provider::ITextRangeProvider>
|
||||
class XamlUiaTextRange : public XamlUiaTextRangeT<XamlUiaTextRange>
|
||||
{
|
||||
public:
|
||||
XamlUiaTextRange(::ITextRangeProvider* uiaProvider, Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple parentProvider) :
|
||||
|
||||
10
src/cascadia/TerminalControl/XamlUiaTextRange.idl
Normal file
10
src/cascadia/TerminalControl/XamlUiaTextRange.idl
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Microsoft.Terminal.Control
|
||||
{
|
||||
[default_interface] runtimeclass XamlUiaTextRange :
|
||||
Windows.UI.Xaml.Automation.Provider.ITextRangeProvider
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,9 @@
|
||||
#include <winrt/Windows.ui.xaml.markup.h>
|
||||
#include <winrt/Windows.ui.xaml.shapes.h>
|
||||
#include <winrt/Windows.ApplicationModel.DataTransfer.h>
|
||||
#include <winrt/Windows.ApplicationModel.Resources.Core.h>
|
||||
#include <winrt/Windows.Storage.h>
|
||||
#include <winrt/Windows.UI.Xaml.Shapes.h>
|
||||
|
||||
#include <winrt/Microsoft.Terminal.TerminalConnection.h>
|
||||
#include <winrt/Microsoft.Terminal.Core.h>
|
||||
|
||||
@@ -45,8 +45,11 @@ Terminal::Terminal() :
|
||||
_snapOnInput{ true },
|
||||
_altGrAliasing{ true },
|
||||
_blockSelection{ false },
|
||||
_markMode{ false },
|
||||
_selectionMode{ SelectionInteractionMode::None },
|
||||
_isTargetingUrl{ false },
|
||||
_selection{ std::nullopt },
|
||||
_selectionEndpoint{ static_cast<SelectionEndpoint>(0) },
|
||||
_anchorInactiveSelectionEndpoint{ false },
|
||||
_taskbarState{ 0 },
|
||||
_taskbarProgress{ 0 },
|
||||
_trimBlockSelection{ false },
|
||||
@@ -561,17 +564,24 @@ bool Terminal::ShouldSendAlternateScroll(const unsigned int uiButton,
|
||||
// Method Description:
|
||||
// - Given a coord, get the URI at that location
|
||||
// Arguments:
|
||||
// - The position
|
||||
std::wstring Terminal::GetHyperlinkAtPosition(const til::point position)
|
||||
// - The position relative to the viewport
|
||||
std::wstring Terminal::GetHyperlinkAtViewportPosition(const til::point viewportPos)
|
||||
{
|
||||
auto attr = _activeBuffer().GetCellDataAt(_ConvertToBufferCell(position))->TextAttr();
|
||||
return GetHyperlinkAtBufferPosition(_ConvertToBufferCell(viewportPos));
|
||||
}
|
||||
|
||||
std::wstring Terminal::GetHyperlinkAtBufferPosition(const til::point bufferPos)
|
||||
{
|
||||
auto attr = _activeBuffer().GetCellDataAt(bufferPos)->TextAttr();
|
||||
if (attr.IsHyperlink())
|
||||
{
|
||||
auto uri = _activeBuffer().GetHyperlinkUriFromId(attr.GetHyperlinkId());
|
||||
return uri;
|
||||
}
|
||||
// also look through our known pattern locations in our pattern interval tree
|
||||
const auto result = GetHyperlinkIntervalFromPosition(position);
|
||||
auto viewportPos = bufferPos;
|
||||
_GetVisibleViewport().ConvertToOrigin(&viewportPos);
|
||||
const auto result = GetHyperlinkIntervalFromViewportPosition(viewportPos);
|
||||
if (result.has_value() && result->value == _hyperlinkPatternId)
|
||||
{
|
||||
const auto start = result->start;
|
||||
@@ -592,23 +602,23 @@ std::wstring Terminal::GetHyperlinkAtPosition(const til::point position)
|
||||
// Method Description:
|
||||
// - Gets the hyperlink ID of the text at the given terminal position
|
||||
// Arguments:
|
||||
// - The position of the text
|
||||
// - The position of the text relative to the viewport
|
||||
// Return value:
|
||||
// - The hyperlink ID
|
||||
uint16_t Terminal::GetHyperlinkIdAtPosition(const til::point position)
|
||||
uint16_t Terminal::GetHyperlinkIdAtViewportPosition(const til::point viewportPos)
|
||||
{
|
||||
return _activeBuffer().GetCellDataAt(_ConvertToBufferCell(position))->TextAttr().GetHyperlinkId();
|
||||
return _activeBuffer().GetCellDataAt(_ConvertToBufferCell(viewportPos))->TextAttr().GetHyperlinkId();
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - Given a position in a URI pattern, gets the start and end coordinates of the URI
|
||||
// Arguments:
|
||||
// - The position
|
||||
// - The position relative to the viewport
|
||||
// Return value:
|
||||
// - The interval representing the start and end coordinates
|
||||
std::optional<PointTree::interval> Terminal::GetHyperlinkIntervalFromPosition(const til::point position)
|
||||
std::optional<PointTree::interval> Terminal::GetHyperlinkIntervalFromViewportPosition(const til::point viewportPos)
|
||||
{
|
||||
const auto results = _patternIntervalTree.findOverlapping({ position.X + 1, position.Y }, position);
|
||||
const auto results = _patternIntervalTree.findOverlapping({ viewportPos.X + 1, viewportPos.Y }, viewportPos);
|
||||
if (results.size() > 0)
|
||||
{
|
||||
for (const auto& result : results)
|
||||
@@ -1369,7 +1379,7 @@ void Terminal::SetCursorOn(const bool isOn)
|
||||
bool Terminal::IsCursorBlinkingAllowed() const noexcept
|
||||
{
|
||||
const auto& cursor = _activeBuffer().GetCursor();
|
||||
return !_markMode && cursor.IsBlinkingAllowed();
|
||||
return _selectionMode != SelectionInteractionMode::Mark && cursor.IsBlinkingAllowed();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -162,9 +162,10 @@ public:
|
||||
|
||||
void FocusChanged(const bool focused) noexcept override;
|
||||
|
||||
std::wstring GetHyperlinkAtPosition(const til::point position);
|
||||
uint16_t GetHyperlinkIdAtPosition(const til::point position);
|
||||
std::optional<interval_tree::IntervalTree<til::point, size_t>::interval> GetHyperlinkIntervalFromPosition(const til::point position);
|
||||
std::wstring GetHyperlinkAtViewportPosition(const til::point viewportPos);
|
||||
std::wstring GetHyperlinkAtBufferPosition(const til::point bufferPos);
|
||||
uint16_t GetHyperlinkIdAtViewportPosition(const til::point viewportPos);
|
||||
std::optional<interval_tree::IntervalTree<til::point, size_t>::interval> GetHyperlinkIntervalFromViewportPosition(const til::point viewportPos);
|
||||
#pragma endregion
|
||||
|
||||
#pragma region IBaseData(base to IRenderData and IUiaData)
|
||||
@@ -233,6 +234,14 @@ public:
|
||||
|
||||
#pragma region TextSelection
|
||||
// These methods are defined in TerminalSelection.cpp
|
||||
enum class SelectionInteractionMode
|
||||
{
|
||||
None,
|
||||
Mouse,
|
||||
Keyboard,
|
||||
Mark
|
||||
};
|
||||
|
||||
enum class SelectionDirection
|
||||
{
|
||||
Left,
|
||||
@@ -241,6 +250,12 @@ public:
|
||||
Down
|
||||
};
|
||||
|
||||
enum class SearchDirection
|
||||
{
|
||||
Forward,
|
||||
Backward
|
||||
};
|
||||
|
||||
enum class SelectionExpansion
|
||||
{
|
||||
Char,
|
||||
@@ -249,17 +264,30 @@ public:
|
||||
Viewport,
|
||||
Buffer
|
||||
};
|
||||
|
||||
enum class SelectionEndpoint
|
||||
{
|
||||
Start = 0x1,
|
||||
End = 0x2
|
||||
};
|
||||
|
||||
void MultiClickSelection(const til::point viewportPos, SelectionExpansion expansionMode);
|
||||
void SetSelectionAnchor(const til::point position);
|
||||
void SetSelectionEnd(const til::point position, std::optional<SelectionExpansion> newExpansionMode = std::nullopt);
|
||||
void SetBlockSelection(const bool isEnabled) noexcept;
|
||||
void UpdateSelection(SelectionDirection direction, SelectionExpansion mode, ControlKeyStates mods);
|
||||
void SelectAll();
|
||||
bool IsInMarkMode() const;
|
||||
SelectionInteractionMode SelectionMode() const noexcept;
|
||||
void SwitchSelectionEndpoint();
|
||||
void ToggleMarkMode();
|
||||
void SelectHyperlink(const SearchDirection dir);
|
||||
bool IsTargetingUrl() const noexcept;
|
||||
|
||||
using UpdateSelectionParams = std::optional<std::pair<SelectionDirection, SelectionExpansion>>;
|
||||
UpdateSelectionParams ConvertKeyEventToUpdateSelectionParams(const ControlKeyStates mods, const WORD vkey) const;
|
||||
til::point SelectionStartForRendering() const;
|
||||
til::point SelectionEndForRendering() const;
|
||||
const SelectionEndpoint SelectionEndpointTarget() const noexcept;
|
||||
|
||||
const TextBuffer::TextAndColor RetrieveSelectedTextFromBuffer(bool trimTrailingWhitespace);
|
||||
#pragma endregion
|
||||
@@ -329,7 +357,10 @@ private:
|
||||
bool _blockSelection;
|
||||
std::wstring _wordDelimiters;
|
||||
SelectionExpansion _multiClickSelectionMode;
|
||||
bool _markMode;
|
||||
SelectionInteractionMode _selectionMode;
|
||||
bool _isTargetingUrl;
|
||||
SelectionEndpoint _selectionEndpoint;
|
||||
bool _anchorInactiveSelectionEndpoint;
|
||||
#pragma endregion
|
||||
|
||||
std::unique_ptr<TextBuffer> _mainBuffer;
|
||||
@@ -403,6 +434,7 @@ private:
|
||||
std::pair<til::point, til::point> _PivotSelection(const til::point targetPos, bool& targetStart) const;
|
||||
std::pair<til::point, til::point> _ExpandSelectionAnchors(std::pair<til::point, til::point> anchors) const;
|
||||
til::point _ConvertToBufferCell(const til::point viewportPos) const;
|
||||
void _ScrollToPoint(const til::point pos);
|
||||
void _MoveByChar(SelectionDirection direction, til::point& pos);
|
||||
void _MoveByWord(SelectionDirection direction, til::point& pos);
|
||||
void _MoveByViewport(SelectionDirection direction, til::point& pos);
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
#include "unicode.hpp"
|
||||
|
||||
using namespace Microsoft::Terminal::Core;
|
||||
using namespace Microsoft::Console::Types;
|
||||
|
||||
DEFINE_ENUM_FLAG_OPERATORS(Terminal::SelectionEndpoint);
|
||||
|
||||
/* Selection Pivot Description:
|
||||
* The pivot helps properly update the selection when a user moves a selection over itself
|
||||
@@ -82,6 +85,49 @@ const til::point Terminal::GetSelectionEnd() const noexcept
|
||||
return _selection->end;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Gets the viewport-relative position of where to draw the marker
|
||||
// for the selection's start endpoint
|
||||
til::point Terminal::SelectionStartForRendering() const
|
||||
{
|
||||
auto pos{ _selection->start };
|
||||
const auto bufferSize{ GetTextBuffer().GetSize() };
|
||||
if (pos.x != bufferSize.Left())
|
||||
{
|
||||
// In general, we need to draw the marker one before the
|
||||
// beginning of the selection.
|
||||
// When we're at the left boundary, we want to
|
||||
// flip the marker, so we skip this step.
|
||||
bufferSize.DecrementInBounds(pos);
|
||||
}
|
||||
pos.Y = base::ClampSub(pos.Y, _VisibleStartIndex());
|
||||
return til::point{ pos };
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Gets the viewport-relative position of where to draw the marker
|
||||
// for the selection's end endpoint
|
||||
til::point Terminal::SelectionEndForRendering() const
|
||||
{
|
||||
auto pos{ _selection->end };
|
||||
const auto bufferSize{ GetTextBuffer().GetSize() };
|
||||
if (pos.x != bufferSize.RightInclusive())
|
||||
{
|
||||
// In general, we need to draw the marker one after the
|
||||
// end of the selection.
|
||||
// When we're at the right boundary, we want to
|
||||
// flip the marker, so we skip this step.
|
||||
bufferSize.IncrementInBounds(pos);
|
||||
}
|
||||
pos.Y = base::ClampSub(pos.Y, _VisibleStartIndex());
|
||||
return til::point{ pos };
|
||||
}
|
||||
|
||||
const Terminal::SelectionEndpoint Terminal::SelectionEndpointTarget() const noexcept
|
||||
{
|
||||
return _selectionEndpoint;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Checks if selection is active
|
||||
// Return Value:
|
||||
@@ -170,6 +216,7 @@ void Terminal::SetSelectionEnd(const til::point viewportPos, std::optional<Selec
|
||||
// expand both anchors
|
||||
std::tie(_selection->start, _selection->end) = expandedAnchors;
|
||||
}
|
||||
_selectionMode = SelectionInteractionMode::Mouse;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -182,7 +229,7 @@ void Terminal::SetSelectionEnd(const til::point viewportPos, std::optional<Selec
|
||||
// - the new start/end for a selection
|
||||
std::pair<til::point, til::point> Terminal::_PivotSelection(const til::point targetPos, bool& targetStart) const
|
||||
{
|
||||
if (targetStart = _activeBuffer().GetSize().CompareInBounds(targetPos, _selection->pivot) <= 0)
|
||||
if (targetStart = targetPos <= _selection->pivot)
|
||||
{
|
||||
// target is before pivot
|
||||
// treat target as start
|
||||
@@ -235,14 +282,14 @@ void Terminal::SetBlockSelection(const bool isEnabled) noexcept
|
||||
_blockSelection = isEnabled;
|
||||
}
|
||||
|
||||
bool Terminal::IsInMarkMode() const
|
||||
Terminal::SelectionInteractionMode Terminal::SelectionMode() const noexcept
|
||||
{
|
||||
return _markMode;
|
||||
return _selectionMode;
|
||||
}
|
||||
|
||||
void Terminal::ToggleMarkMode()
|
||||
{
|
||||
if (_markMode)
|
||||
if (_selectionMode == SelectionInteractionMode::Mark)
|
||||
{
|
||||
// Exit Mark Mode
|
||||
ClearSelection();
|
||||
@@ -257,14 +304,195 @@ void Terminal::ToggleMarkMode()
|
||||
_selection->start = cursorPos;
|
||||
_selection->end = cursorPos;
|
||||
_selection->pivot = cursorPos;
|
||||
_markMode = true;
|
||||
_ScrollToPoint(cursorPos);
|
||||
_selectionMode = SelectionInteractionMode::Mark;
|
||||
_blockSelection = false;
|
||||
_isTargetingUrl = false;
|
||||
WI_SetAllFlags(_selectionEndpoint, SelectionEndpoint::Start | SelectionEndpoint::End);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - switch the targeted selection endpoint with the other one (i.e. start <--> end)
|
||||
void Terminal::SwitchSelectionEndpoint()
|
||||
{
|
||||
if (IsSelectionActive())
|
||||
{
|
||||
if (WI_IsFlagSet(_selectionEndpoint, SelectionEndpoint::Start) && WI_IsFlagSet(_selectionEndpoint, SelectionEndpoint::End))
|
||||
{
|
||||
// moving cursor --> anchor start, move end
|
||||
_selectionEndpoint = SelectionEndpoint::End;
|
||||
_anchorInactiveSelectionEndpoint = true;
|
||||
}
|
||||
else if (WI_IsFlagSet(_selectionEndpoint, SelectionEndpoint::End))
|
||||
{
|
||||
// moving end --> now we're moving start
|
||||
_selectionEndpoint = SelectionEndpoint::Start;
|
||||
_selection->pivot = _selection->end;
|
||||
}
|
||||
else if (WI_IsFlagSet(_selectionEndpoint, SelectionEndpoint::Start))
|
||||
{
|
||||
// moving start --> now we're moving end
|
||||
_selectionEndpoint = SelectionEndpoint::End;
|
||||
_selection->pivot = _selection->start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - selects the next/previous hyperlink, if one is available
|
||||
// Arguments:
|
||||
// - dir: the direction we're scanning the buffer in to find the hyperlink of interest
|
||||
// Return Value:
|
||||
// - true if we found a hyperlink to select (and selected it). False otherwise.
|
||||
void Terminal::SelectHyperlink(const SearchDirection dir)
|
||||
{
|
||||
if (_selectionMode != SelectionInteractionMode::Mark)
|
||||
{
|
||||
// This feature only works in mark mode
|
||||
_isTargetingUrl = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// 0. Useful tools/vars
|
||||
const auto bufferSize = _activeBuffer().GetSize();
|
||||
const auto viewportHeight = _GetMutableViewport().Height();
|
||||
|
||||
// The patterns are stored relative to the "search area". Initially, this search area will be the viewport,
|
||||
// but as we progressively search through more of the buffer, this will change.
|
||||
// Keep track of the search area here, and use the lambda below to convert points to the search area coordinate space.
|
||||
auto searchArea = _GetVisibleViewport();
|
||||
auto convertToSearchArea = [&searchArea](const til::point pt) {
|
||||
auto copy = pt;
|
||||
searchArea.ConvertToOrigin(©);
|
||||
return copy;
|
||||
};
|
||||
|
||||
// extracts the next/previous hyperlink from the list of hyperlink ranges provided
|
||||
auto extractResultFromList = [&](std::vector<interval_tree::Interval<til::point, size_t>>& list) {
|
||||
const auto selectionStartInSearchArea = convertToSearchArea(_selection->start);
|
||||
|
||||
std::optional<std::pair<til::point, til::point>> resultFromList;
|
||||
if (!list.empty())
|
||||
{
|
||||
if (dir == SearchDirection::Forward)
|
||||
{
|
||||
// pattern tree includes the currently selected range when going forward,
|
||||
// so we need to check if we're pointing to that one before returning it.
|
||||
auto range = list.front();
|
||||
if (_isTargetingUrl && range.start == selectionStartInSearchArea)
|
||||
{
|
||||
if (list.size() > 1)
|
||||
{
|
||||
// if we're pointing to the currently selected URL,
|
||||
// pick the next one.
|
||||
range = til::at(list, 1);
|
||||
resultFromList = { range.start, range.stop };
|
||||
}
|
||||
else
|
||||
{
|
||||
// LOAD-BEARING: the only range here is the one that's currently selected.
|
||||
// Make sure this is set to nullopt so that we keep searching through the buffer.
|
||||
resultFromList = std::nullopt;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// not on currently selected range, return the first one
|
||||
resultFromList = { range.start, range.stop };
|
||||
}
|
||||
}
|
||||
else if (dir == SearchDirection::Backward)
|
||||
{
|
||||
// moving backwards excludes the currently selected range,
|
||||
// simply return the last one in the list as it's ordered
|
||||
const auto range = list.back();
|
||||
resultFromList = { range.start, range.stop };
|
||||
}
|
||||
}
|
||||
|
||||
// pattern tree stores everything as viewport coords,
|
||||
// so we need to convert them on the way out
|
||||
if (resultFromList)
|
||||
{
|
||||
searchArea.ConvertFromOrigin(&resultFromList->first);
|
||||
searchArea.ConvertFromOrigin(&resultFromList->second);
|
||||
}
|
||||
return resultFromList;
|
||||
};
|
||||
|
||||
// 1. Look for the hyperlink
|
||||
til::point searchStart = dir == SearchDirection::Forward ? _selection->start : til::point{ bufferSize.Left(), _VisibleStartIndex() };
|
||||
til::point searchEnd = dir == SearchDirection::Forward ? til::point{ bufferSize.RightInclusive(), _VisibleEndIndex() } : _selection->start;
|
||||
|
||||
// 1.A) Try searching the current viewport (no scrolling required)
|
||||
auto resultList = _patternIntervalTree.findContained(convertToSearchArea(searchStart), convertToSearchArea(searchEnd));
|
||||
std::optional<std::pair<til::point, til::point>> result = extractResultFromList(resultList);
|
||||
if (!result)
|
||||
{
|
||||
// 1.B) Incrementally search through more of the space
|
||||
if (dir == SearchDirection::Forward)
|
||||
{
|
||||
searchStart = { bufferSize.Left(), searchEnd.y + 1 };
|
||||
searchEnd = { bufferSize.RightInclusive(), std::min(searchStart.y + viewportHeight, ViewEndIndex()) };
|
||||
}
|
||||
else
|
||||
{
|
||||
searchEnd = { bufferSize.RightInclusive(), searchStart.y - 1 };
|
||||
searchStart = { bufferSize.Left(), std::max(searchStart.y - viewportHeight, bufferSize.Top()) };
|
||||
}
|
||||
searchArea = Viewport::FromDimensions(searchStart, searchEnd.x + 1, searchEnd.y + 1);
|
||||
|
||||
const til::point bufferStart{ bufferSize.Origin() };
|
||||
const til::point bufferEnd{ bufferSize.RightInclusive(), ViewEndIndex() };
|
||||
while (!result && bufferSize.IsInBounds(searchStart) && bufferSize.IsInBounds(searchEnd) && searchStart <= searchEnd && bufferStart <= searchStart && searchEnd <= bufferEnd)
|
||||
{
|
||||
auto patterns = _activeBuffer().GetPatterns(searchStart.y, searchEnd.y);
|
||||
resultList = patterns.findContained(convertToSearchArea(searchStart), convertToSearchArea(searchEnd));
|
||||
result = extractResultFromList(resultList);
|
||||
if (!result)
|
||||
{
|
||||
if (dir == SearchDirection::Forward)
|
||||
{
|
||||
searchStart.y += 1;
|
||||
searchEnd.y = std::min(searchStart.y + viewportHeight, ViewEndIndex());
|
||||
}
|
||||
else
|
||||
{
|
||||
searchEnd.y -= 1;
|
||||
searchStart.y = std::max(searchEnd.y - viewportHeight, bufferSize.Top());
|
||||
}
|
||||
searchArea = Viewport::FromDimensions(searchStart, searchEnd.x + 1, searchEnd.y + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 1.C) Nothing was found. Bail!
|
||||
if (!result.has_value())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Select the hyperlink
|
||||
_selection->start = result->first;
|
||||
_selection->pivot = result->first;
|
||||
_selection->end = result->second;
|
||||
bufferSize.DecrementInBounds(_selection->end);
|
||||
_isTargetingUrl = true;
|
||||
_selectionEndpoint = SelectionEndpoint::End;
|
||||
|
||||
// 3. Scroll to the selected area (if necessary)
|
||||
_ScrollToPoint(_selection->end);
|
||||
}
|
||||
|
||||
bool Terminal::IsTargetingUrl() const noexcept
|
||||
{
|
||||
return _isTargetingUrl;
|
||||
}
|
||||
|
||||
Terminal::UpdateSelectionParams Terminal::ConvertKeyEventToUpdateSelectionParams(const ControlKeyStates mods, const WORD vkey) const
|
||||
{
|
||||
if ((_markMode || mods.IsShiftPressed()) && !mods.IsAltPressed())
|
||||
if ((_selectionMode == SelectionInteractionMode::Mark || mods.IsShiftPressed()) && !mods.IsAltPressed())
|
||||
{
|
||||
if (mods.IsCtrlPressed())
|
||||
{
|
||||
@@ -318,14 +546,34 @@ Terminal::UpdateSelectionParams Terminal::ConvertKeyEventToUpdateSelectionParams
|
||||
// - mods: the key modifiers pressed when performing this update
|
||||
void Terminal::UpdateSelection(SelectionDirection direction, SelectionExpansion mode, ControlKeyStates mods)
|
||||
{
|
||||
// 1. Figure out which endpoint to update
|
||||
// If we're in mark mode, shift dictates whether you are moving the end or not.
|
||||
// Otherwise, we're updating an existing selection, so one of the endpoints is the pivot,
|
||||
// signifying that the other endpoint is the one we want to move.
|
||||
const auto movingEnd{ _markMode ? mods.IsShiftPressed() : _selection->start == _selection->pivot };
|
||||
auto targetPos{ movingEnd ? _selection->end : _selection->start };
|
||||
// This is a special variable used to track if we should move the cursor when in mark mode.
|
||||
// We have special functionality where if you use the "switchSelectionEndpoint" action
|
||||
// when in mark mode (moving the cursor), we anchor an endpoint and you can use the
|
||||
// plain arrow keys to move the endpoint. This way, you don't have to hold shift anymore!
|
||||
const bool shouldMoveBothEndpoints = _selectionMode == SelectionInteractionMode::Mark && !_anchorInactiveSelectionEndpoint && !mods.IsShiftPressed();
|
||||
|
||||
// 2. Perform the movement
|
||||
// 1. Figure out which endpoint to update
|
||||
// [Mark Mode]
|
||||
// - shift pressed --> only move "end" (or "start" if "pivot" == "end")
|
||||
// - otherwise --> move both "start" and "end" (moving cursor)
|
||||
// [Quick Edit]
|
||||
// - just move "end" (or "start" if "pivot" == "end")
|
||||
_selectionEndpoint = static_cast<SelectionEndpoint>(0);
|
||||
if (shouldMoveBothEndpoints)
|
||||
{
|
||||
WI_SetAllFlags(_selectionEndpoint, SelectionEndpoint::Start | SelectionEndpoint::End);
|
||||
}
|
||||
else if (_selection->start == _selection->pivot)
|
||||
{
|
||||
WI_SetFlag(_selectionEndpoint, SelectionEndpoint::End);
|
||||
}
|
||||
else if (_selection->end == _selection->pivot)
|
||||
{
|
||||
WI_SetFlag(_selectionEndpoint, SelectionEndpoint::Start);
|
||||
}
|
||||
auto targetPos{ WI_IsFlagSet(_selectionEndpoint, SelectionEndpoint::Start) ? _selection->start : _selection->end };
|
||||
|
||||
// 2 Perform the movement
|
||||
switch (mode)
|
||||
{
|
||||
case SelectionExpansion::Char:
|
||||
@@ -342,43 +590,31 @@ void Terminal::UpdateSelection(SelectionDirection direction, SelectionExpansion
|
||||
break;
|
||||
}
|
||||
|
||||
// 3. Actually modify the selection
|
||||
// NOTE: targetStart doesn't matter here
|
||||
if (_markMode)
|
||||
// 3. Actually modify the selection state
|
||||
_isTargetingUrl = false;
|
||||
_selectionMode = std::max(_selectionMode, SelectionInteractionMode::Keyboard);
|
||||
if (shouldMoveBothEndpoints)
|
||||
{
|
||||
// [Mark Mode]
|
||||
// - moveSelectionEnd --> just move end (i.e. shift + arrow keys)
|
||||
// - !moveSelectionEnd --> move all three (i.e. just use arrow keys)
|
||||
// [Mark Mode] + shift unpressed --> move all three (i.e. just use arrow keys)
|
||||
_selection->start = targetPos;
|
||||
_selection->end = targetPos;
|
||||
if (!movingEnd)
|
||||
{
|
||||
_selection->start = targetPos;
|
||||
_selection->pivot = targetPos;
|
||||
}
|
||||
_selection->pivot = targetPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto targetStart = false;
|
||||
// [Mark Mode] + shift --> updating a standard selection
|
||||
// This is also standard quick-edit modification
|
||||
bool targetStart = false;
|
||||
std::tie(_selection->start, _selection->end) = _PivotSelection(targetPos, targetStart);
|
||||
|
||||
// IMPORTANT! Pivoting the selection here might have changed which endpoint we're targeting.
|
||||
// So let's set the targeted endpoint again.
|
||||
WI_UpdateFlag(_selectionEndpoint, SelectionEndpoint::Start, targetStart);
|
||||
WI_UpdateFlag(_selectionEndpoint, SelectionEndpoint::End, !targetStart);
|
||||
}
|
||||
|
||||
// 4. Scroll (if necessary)
|
||||
if (const auto viewport = _GetVisibleViewport(); !viewport.IsInBounds(targetPos))
|
||||
{
|
||||
if (const auto amtAboveView = viewport.Top() - targetPos.Y; amtAboveView > 0)
|
||||
{
|
||||
// anchor is above visible viewport, scroll by that amount
|
||||
_scrollOffset += amtAboveView;
|
||||
}
|
||||
else
|
||||
{
|
||||
// anchor is below visible viewport, scroll by that amount
|
||||
const auto amtBelowView = targetPos.Y - viewport.BottomInclusive();
|
||||
_scrollOffset -= amtBelowView;
|
||||
}
|
||||
_NotifyScrollEvent();
|
||||
_activeBuffer().TriggerScroll();
|
||||
}
|
||||
_ScrollToPoint(targetPos);
|
||||
}
|
||||
|
||||
void Terminal::SelectAll()
|
||||
@@ -388,6 +624,7 @@ void Terminal::SelectAll()
|
||||
_selection->start = bufferSize.Origin();
|
||||
_selection->end = { bufferSize.RightInclusive(), _GetMutableViewport().BottomInclusive() };
|
||||
_selection->pivot = _selection->end;
|
||||
_selectionMode = SelectionInteractionMode::Keyboard;
|
||||
}
|
||||
|
||||
void Terminal::_MoveByChar(SelectionDirection direction, til::point& pos)
|
||||
@@ -405,13 +642,16 @@ void Terminal::_MoveByChar(SelectionDirection direction, til::point& pos)
|
||||
case SelectionDirection::Up:
|
||||
{
|
||||
const auto bufferSize{ _activeBuffer().GetSize() };
|
||||
pos = { pos.X, std::clamp(pos.Y - 1, bufferSize.Top(), bufferSize.BottomInclusive()) };
|
||||
const auto newY{ pos.Y - 1 };
|
||||
pos = newY < bufferSize.Top() ? bufferSize.Origin() : til::point{ pos.X, newY };
|
||||
break;
|
||||
}
|
||||
case SelectionDirection::Down:
|
||||
{
|
||||
const auto bufferSize{ _activeBuffer().GetSize() };
|
||||
pos = { pos.X, std::clamp(pos.Y + 1, bufferSize.Top(), bufferSize.BottomInclusive()) };
|
||||
const auto mutableBottom{ _GetMutableViewport().BottomInclusive() };
|
||||
const auto newY{ pos.Y + 1 };
|
||||
pos = newY > mutableBottom ? til::point{ bufferSize.RightInclusive(), mutableBottom } : til::point{ pos.X, newY };
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -424,7 +664,7 @@ void Terminal::_MoveByWord(SelectionDirection direction, til::point& pos)
|
||||
case SelectionDirection::Left:
|
||||
{
|
||||
const auto wordStartPos{ _activeBuffer().GetWordStart(pos, _wordDelimiters) };
|
||||
if (_activeBuffer().GetSize().CompareInBounds(_selection->pivot, pos) < 0)
|
||||
if (_selection->pivot < pos)
|
||||
{
|
||||
// If we're moving towards the pivot, move one more cell
|
||||
pos = wordStartPos;
|
||||
@@ -447,7 +687,7 @@ void Terminal::_MoveByWord(SelectionDirection direction, til::point& pos)
|
||||
case SelectionDirection::Right:
|
||||
{
|
||||
const auto wordEndPos{ _activeBuffer().GetWordEnd(pos, _wordDelimiters) };
|
||||
if (_activeBuffer().GetSize().CompareInBounds(pos, _selection->pivot) < 0)
|
||||
if (pos < _selection->pivot)
|
||||
{
|
||||
// If we're moving towards the pivot, move one more cell
|
||||
pos = _activeBuffer().GetWordEnd(pos, _wordDelimiters);
|
||||
@@ -529,7 +769,10 @@ void Terminal::_MoveByBuffer(SelectionDirection direction, til::point& pos)
|
||||
void Terminal::ClearSelection()
|
||||
{
|
||||
_selection = std::nullopt;
|
||||
_markMode = false;
|
||||
_selectionMode = SelectionInteractionMode::None;
|
||||
_isTargetingUrl = false;
|
||||
_selectionEndpoint = static_cast<SelectionEndpoint>(0);
|
||||
_anchorInactiveSelectionEndpoint = false;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -572,6 +815,30 @@ til::point Terminal::_ConvertToBufferCell(const til::point viewportPos) const
|
||||
return bufferPos;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - if necessary, scroll the viewport such that the given point is visible
|
||||
// Arguments:
|
||||
// - pos: a coordinate relative to the buffer (not viewport)
|
||||
void Terminal::_ScrollToPoint(const til::point pos)
|
||||
{
|
||||
if (const auto visibleViewport = _GetVisibleViewport(); !visibleViewport.IsInBounds(pos))
|
||||
{
|
||||
if (const auto amtAboveView = visibleViewport.Top() - pos.Y; amtAboveView > 0)
|
||||
{
|
||||
// anchor is above visible viewport, scroll by that amount
|
||||
_scrollOffset += amtAboveView;
|
||||
}
|
||||
else
|
||||
{
|
||||
// anchor is below visible viewport, scroll by that amount
|
||||
const auto amtBelowView = pos.Y - visibleViewport.BottomInclusive();
|
||||
_scrollOffset -= amtBelowView;
|
||||
}
|
||||
_NotifyScrollEvent();
|
||||
_activeBuffer().TriggerScroll();
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - This method won't be used. We just throw and do nothing. For now we
|
||||
// need this method to implement UiaData interface
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "EnumEntry.h"
|
||||
#include "GlobalAppearance.h"
|
||||
#include "GlobalAppearance.g.cpp"
|
||||
#include "GlobalAppearancePageNavigationState.g.cpp"
|
||||
|
||||
#include <LibraryResources.h>
|
||||
#include <WtExeUtils.h>
|
||||
@@ -19,180 +17,13 @@ using namespace winrt::Windows::Foundation::Collections;
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
// For ComboBox an empty SelectedItem string denotes no selection.
|
||||
// What we want instead is for "Use system language" to be selected by default.
|
||||
// --> "und" is synonymous for "Use system language".
|
||||
constexpr std::wstring_view systemLanguageTag{ L"und" };
|
||||
|
||||
static constexpr std::array appLanguageTags{
|
||||
L"en-US",
|
||||
L"de-DE",
|
||||
L"es-ES",
|
||||
L"fr-FR",
|
||||
L"it-IT",
|
||||
L"ja",
|
||||
L"ko",
|
||||
L"pt-BR",
|
||||
L"qps-PLOC",
|
||||
L"qps-PLOCA",
|
||||
L"qps-PLOCM",
|
||||
L"ru",
|
||||
L"zh-Hans",
|
||||
L"zh-Hant",
|
||||
};
|
||||
|
||||
GlobalAppearance::GlobalAppearance()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
INITIALIZE_BINDABLE_ENUM_SETTING(Theme, ElementTheme, winrt::Windows::UI::Xaml::ElementTheme, L"Globals_Theme", L"Content");
|
||||
INITIALIZE_BINDABLE_ENUM_SETTING(TabWidthMode, TabViewWidthMode, winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, L"Globals_TabWidthMode", L"Content");
|
||||
}
|
||||
|
||||
void GlobalAppearance::OnNavigatedTo(const NavigationEventArgs& e)
|
||||
{
|
||||
_State = e.Parameter().as<Editor::GlobalAppearancePageNavigationState>();
|
||||
_ViewModel = e.Parameter().as<Editor::GlobalAppearanceViewModel>();
|
||||
}
|
||||
|
||||
winrt::hstring GlobalAppearance::LanguageDisplayConverter(const winrt::hstring& tag)
|
||||
{
|
||||
if (tag == systemLanguageTag)
|
||||
{
|
||||
return RS_(L"Globals_LanguageDefault");
|
||||
}
|
||||
|
||||
winrt::Windows::Globalization::Language language{ tag };
|
||||
return language.NativeName();
|
||||
}
|
||||
|
||||
// Returns whether the language selector is available/shown.
|
||||
//
|
||||
// winrt::Windows::Globalization::ApplicationLanguages::PrimaryLanguageOverride()
|
||||
// doesn't work for unpackaged applications. The corresponding code in TerminalApp is disabled.
|
||||
// It would be confusing for our users if we presented a dysfunctional language selector.
|
||||
bool GlobalAppearance::LanguageSelectorAvailable()
|
||||
{
|
||||
return IsPackaged();
|
||||
}
|
||||
|
||||
// Returns the list of languages the user may override the application language with.
|
||||
// The returned list are BCP 47 language tags like {"und", "en-US", "de-DE", "es-ES", ...}.
|
||||
// "und" is short for "undefined" and is synonymous for "Use system language" in this code.
|
||||
winrt::Windows::Foundation::Collections::IObservableVector<winrt::hstring> GlobalAppearance::LanguageList()
|
||||
{
|
||||
if (_languageList)
|
||||
{
|
||||
return _languageList;
|
||||
}
|
||||
|
||||
if (!LanguageSelectorAvailable())
|
||||
{
|
||||
_languageList = {};
|
||||
return _languageList;
|
||||
}
|
||||
|
||||
// In order to return the language list this code does the following:
|
||||
// [1] Get all possible languages we want to allow the user to choose.
|
||||
// We have to acquire languages from multiple sources, creating duplicates. See below at [1].
|
||||
// [2] Sort languages by their ASCII tags, forcing the UI in a consistent/stable order.
|
||||
// I wanted to sort the localized language names initially, but it turned out to be complex.
|
||||
// [3] Remove potential duplicates in our language list from [1].
|
||||
// We don't want to have en-US twice in the list, do we?
|
||||
// [4] Optionally remove unwanted language tags (like pseudo-localizations).
|
||||
|
||||
std::vector<winrt::hstring> tags;
|
||||
|
||||
// [1]:
|
||||
{
|
||||
// ManifestLanguages contains languages the app ships with.
|
||||
// Unfortunately, we cannot use this source. Our manifest must contain the
|
||||
// ~100 languages that are localized for the shell extension and start menu
|
||||
// presentation so we align with Windows display languages for those surfaces.
|
||||
// However, the actual content of our application is limited to a much smaller
|
||||
// subset of approximately 14 languages. As such, we will code the limited
|
||||
// subset of languages that we support for selection within the Settings
|
||||
// dropdown to steer users towards the ones that we can display in the app.
|
||||
|
||||
// As per the function definition, the first item
|
||||
// is always "Use system language" ("und").
|
||||
tags.emplace_back(systemLanguageTag);
|
||||
|
||||
// Add our hardcoded languages after the system definition.
|
||||
for (const auto& v : appLanguageTags)
|
||||
{
|
||||
tags.push_back(v);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: The size of tags is always >0, due to tags[0] being hardcoded to "und".
|
||||
const auto tagsBegin = ++tags.begin();
|
||||
const auto tagsEnd = tags.end();
|
||||
|
||||
// [2]:
|
||||
std::sort(tagsBegin, tagsEnd);
|
||||
|
||||
// I'd love for both, std::unique and std::remove_if, to occur in a single loop,
|
||||
// but the code turned out to be complex and even less maintainable, so I gave up.
|
||||
{
|
||||
// [3] part 1:
|
||||
auto it = std::unique(tagsBegin, tagsEnd);
|
||||
|
||||
// The qps- languages are useful for testing ("pseudo-localization").
|
||||
// --> Leave them in if debug features are enabled.
|
||||
if (!_State.Globals().DebugFeaturesEnabled())
|
||||
{
|
||||
// [4] part 1:
|
||||
it = std::remove_if(tagsBegin, it, [](const winrt::hstring& tag) -> bool {
|
||||
return til::starts_with(tag, L"qps-");
|
||||
});
|
||||
}
|
||||
|
||||
// [3], [4] part 2 (completing the so called "erase-remove idiom"):
|
||||
tags.erase(it, tagsEnd);
|
||||
}
|
||||
|
||||
_languageList = winrt::single_threaded_observable_vector(std::move(tags));
|
||||
return _languageList;
|
||||
}
|
||||
|
||||
winrt::Windows::Foundation::IInspectable GlobalAppearance::CurrentLanguage()
|
||||
{
|
||||
if (_currentLanguage)
|
||||
{
|
||||
return _currentLanguage;
|
||||
}
|
||||
|
||||
if (!LanguageSelectorAvailable())
|
||||
{
|
||||
_currentLanguage = {};
|
||||
return _currentLanguage;
|
||||
}
|
||||
|
||||
// NOTE: PrimaryLanguageOverride throws if this instance is unpackaged.
|
||||
auto currentLanguage = winrt::Windows::Globalization::ApplicationLanguages::PrimaryLanguageOverride();
|
||||
if (currentLanguage.empty())
|
||||
{
|
||||
currentLanguage = systemLanguageTag;
|
||||
}
|
||||
|
||||
_currentLanguage = winrt::box_value(currentLanguage);
|
||||
return _currentLanguage;
|
||||
}
|
||||
|
||||
void GlobalAppearance::CurrentLanguage(const winrt::Windows::Foundation::IInspectable& tag)
|
||||
{
|
||||
_currentLanguage = tag;
|
||||
|
||||
const auto currentLanguage = winrt::unbox_value<winrt::hstring>(_currentLanguage);
|
||||
const auto globals = _State.Globals();
|
||||
if (currentLanguage == systemLanguageTag)
|
||||
{
|
||||
globals.ClearLanguage();
|
||||
}
|
||||
else
|
||||
{
|
||||
globals.Language(currentLanguage);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,20 +4,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "GlobalAppearance.g.h"
|
||||
#include "GlobalAppearancePageNavigationState.g.h"
|
||||
#include "Utils.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
struct GlobalAppearancePageNavigationState : GlobalAppearancePageNavigationStateT<GlobalAppearancePageNavigationState>
|
||||
{
|
||||
public:
|
||||
GlobalAppearancePageNavigationState(const Model::GlobalAppSettings& settings) :
|
||||
_Globals{ settings } {}
|
||||
|
||||
WINRT_PROPERTY(Model::GlobalAppSettings, Globals, nullptr)
|
||||
};
|
||||
|
||||
struct GlobalAppearance : public HasScrollViewer<GlobalAppearance>, GlobalAppearanceT<GlobalAppearance>
|
||||
{
|
||||
public:
|
||||
@@ -25,24 +15,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
|
||||
void OnNavigatedTo(const winrt::Windows::UI::Xaml::Navigation::NavigationEventArgs& e);
|
||||
|
||||
WINRT_PROPERTY(Editor::GlobalAppearancePageNavigationState, State, nullptr);
|
||||
GETSET_BINDABLE_ENUM_SETTING(Theme, winrt::Windows::UI::Xaml::ElementTheme, State().Globals().Theme);
|
||||
GETSET_BINDABLE_ENUM_SETTING(TabWidthMode, winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, State().Globals().TabWidthMode);
|
||||
|
||||
public:
|
||||
// LanguageDisplayConverter maps the given BCP 47 tag to a localized string.
|
||||
// For instance "en-US" produces "English (United States)", while "de-DE" produces
|
||||
// "Deutsch (Deutschland)". This works independently of the user's locale.
|
||||
static winrt::hstring LanguageDisplayConverter(const winrt::hstring& tag);
|
||||
|
||||
bool LanguageSelectorAvailable();
|
||||
winrt::Windows::Foundation::Collections::IObservableVector<winrt::hstring> LanguageList();
|
||||
winrt::Windows::Foundation::IInspectable CurrentLanguage();
|
||||
void CurrentLanguage(const winrt::Windows::Foundation::IInspectable& tag);
|
||||
|
||||
private:
|
||||
winrt::Windows::Foundation::Collections::IObservableVector<winrt::hstring> _languageList;
|
||||
winrt::Windows::Foundation::IInspectable _currentLanguage;
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
WINRT_OBSERVABLE_PROPERTY(Editor::GlobalAppearanceViewModel, ViewModel, _PropertyChangedHandlers, nullptr);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,29 +1,13 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import "EnumEntry.idl";
|
||||
import "GlobalAppearanceViewModel.idl";
|
||||
|
||||
namespace Microsoft.Terminal.Settings.Editor
|
||||
{
|
||||
runtimeclass GlobalAppearancePageNavigationState
|
||||
{
|
||||
Microsoft.Terminal.Settings.Model.GlobalAppSettings Globals;
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass GlobalAppearance : Windows.UI.Xaml.Controls.Page
|
||||
{
|
||||
GlobalAppearance();
|
||||
GlobalAppearancePageNavigationState State { get; };
|
||||
|
||||
static String LanguageDisplayConverter(String tag);
|
||||
Boolean LanguageSelectorAvailable { get; };
|
||||
Windows.Foundation.Collections.IObservableVector<String> LanguageList { get; };
|
||||
IInspectable CurrentLanguage;
|
||||
|
||||
IInspectable CurrentTheme;
|
||||
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> ThemeList { get; };
|
||||
|
||||
IInspectable CurrentTabWidthMode;
|
||||
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> TabWidthModeList { get; };
|
||||
GlobalAppearanceViewModel ViewModel { get; };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:Microsoft.Terminal.Settings.Editor"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:model="using:Microsoft.Terminal.Settings.Model"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
mc:Ignorable="d">
|
||||
|
||||
@@ -28,12 +29,12 @@
|
||||
<StackPanel Style="{StaticResource SettingsStackStyle}">
|
||||
<!-- Language -->
|
||||
<local:SettingContainer x:Uid="Globals_Language"
|
||||
Visibility="{x:Bind LanguageSelectorAvailable}">
|
||||
<ComboBox ItemsSource="{x:Bind LanguageList}"
|
||||
SelectedItem="{x:Bind CurrentLanguage, Mode=TwoWay}">
|
||||
Visibility="{x:Bind ViewModel.LanguageSelectorAvailable}">
|
||||
<ComboBox ItemsSource="{x:Bind ViewModel.LanguageList}"
|
||||
SelectedItem="{x:Bind ViewModel.CurrentLanguage, Mode=TwoWay}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="x:String">
|
||||
<TextBlock Text="{x:Bind local:GlobalAppearance.LanguageDisplayConverter((x:String))}" />
|
||||
<TextBlock Text="{x:Bind local:GlobalAppearanceViewModel.LanguageDisplayConverter((x:String))}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
@@ -42,39 +43,53 @@
|
||||
<!-- Theme -->
|
||||
<local:SettingContainer x:Uid="Globals_Theme">
|
||||
<ComboBox AutomationProperties.AccessibilityView="Content"
|
||||
ItemTemplate="{StaticResource EnumComboBoxTemplate}"
|
||||
ItemsSource="{x:Bind ThemeList, Mode=OneWay}"
|
||||
SelectedItem="{x:Bind CurrentTheme, Mode=TwoWay}"
|
||||
Style="{StaticResource ComboBoxSettingStyle}" />
|
||||
ItemsSource="{x:Bind ViewModel.ThemeList, Mode=OneWay}"
|
||||
SelectedItem="{x:Bind ViewModel.CurrentTheme, Mode=TwoWay}"
|
||||
Style="{StaticResource ComboBoxSettingStyle}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="model:Theme">
|
||||
<TextBlock Text="{x:Bind local:GlobalAppearanceViewModel.ThemeNameConverter((model:Theme)), Mode=OneWay}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</local:SettingContainer>
|
||||
|
||||
<!-- Always show tabs -->
|
||||
<local:SettingContainer x:Uid="Globals_AlwaysShowTabs">
|
||||
<ToggleSwitch IsOn="{x:Bind State.Globals.AlwaysShowTabs, Mode=TwoWay}"
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AlwaysShowTabs, Mode=TwoWay}"
|
||||
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
|
||||
</local:SettingContainer>
|
||||
|
||||
<!-- Position of new tab -->
|
||||
<local:SettingContainer x:Uid="Globals_NewTabPosition">
|
||||
<ComboBox AutomationProperties.AccessibilityView="Content"
|
||||
ItemTemplate="{StaticResource EnumComboBoxTemplate}"
|
||||
ItemsSource="{x:Bind ViewModel.NewTabPositionList, Mode=OneWay}"
|
||||
SelectedItem="{x:Bind ViewModel.CurrentNewTabPosition, Mode=TwoWay}"
|
||||
Style="{StaticResource ComboBoxSettingStyle}" />
|
||||
</local:SettingContainer>
|
||||
|
||||
<!-- Show Titlebar -->
|
||||
<local:SettingContainer x:Uid="Globals_ShowTitlebar">
|
||||
<ToggleSwitch IsOn="{x:Bind State.Globals.ShowTabsInTitlebar, Mode=TwoWay}"
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.ShowTabsInTitlebar, Mode=TwoWay}"
|
||||
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
|
||||
</local:SettingContainer>
|
||||
|
||||
<!-- Show Acrylic in Tab Row -->
|
||||
<local:SettingContainer x:Uid="Globals_AcrylicTabRow">
|
||||
<ToggleSwitch IsOn="{x:Bind State.Globals.UseAcrylicInTabRow, Mode=TwoWay}"
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.UseAcrylicInTabRow, Mode=TwoWay}"
|
||||
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
|
||||
</local:SettingContainer>
|
||||
|
||||
<!-- Show Title in Titlebar -->
|
||||
<local:SettingContainer x:Uid="Globals_ShowTitleInTitlebar">
|
||||
<ToggleSwitch IsOn="{x:Bind State.Globals.ShowTitleInTitlebar, Mode=TwoWay}"
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.ShowTitleInTitlebar, Mode=TwoWay}"
|
||||
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
|
||||
</local:SettingContainer>
|
||||
|
||||
<!-- Always on Top -->
|
||||
<local:SettingContainer x:Uid="Globals_AlwaysOnTop">
|
||||
<ToggleSwitch IsOn="{x:Bind State.Globals.AlwaysOnTop, Mode=TwoWay}"
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AlwaysOnTop, Mode=TwoWay}"
|
||||
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
|
||||
</local:SettingContainer>
|
||||
|
||||
@@ -82,27 +97,27 @@
|
||||
<local:SettingContainer x:Uid="Globals_TabWidthMode">
|
||||
<ComboBox AutomationProperties.AccessibilityView="Content"
|
||||
ItemTemplate="{StaticResource EnumComboBoxTemplate}"
|
||||
ItemsSource="{x:Bind TabWidthModeList, Mode=OneWay}"
|
||||
SelectedItem="{x:Bind CurrentTabWidthMode, Mode=TwoWay}"
|
||||
ItemsSource="{x:Bind ViewModel.TabWidthModeList, Mode=OneWay}"
|
||||
SelectedItem="{x:Bind ViewModel.CurrentTabWidthMode, Mode=TwoWay}"
|
||||
Style="{StaticResource ComboBoxSettingStyle}" />
|
||||
</local:SettingContainer>
|
||||
|
||||
<!-- Disable Animations -->
|
||||
<!-- NOTE: the UID is "DisablePaneAnimationsReversed" not "DisablePaneAnimations". See GH#9124 for more details. -->
|
||||
<local:SettingContainer x:Uid="Globals_DisableAnimationsReversed">
|
||||
<ToggleSwitch IsOn="{x:Bind local:Converters.InvertBoolean(State.Globals.DisableAnimations), BindBack=State.Globals.SetInvertedDisableAnimationsValue, Mode=TwoWay}"
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.InvertedDisableAnimations, Mode=TwoWay}"
|
||||
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
|
||||
</local:SettingContainer>
|
||||
|
||||
<!-- Always Show Notification Icon -->
|
||||
<local:SettingContainer x:Uid="Globals_AlwaysShowNotificationIcon">
|
||||
<ToggleSwitch IsOn="{x:Bind State.Globals.AlwaysShowNotificationIcon, Mode=TwoWay}"
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AlwaysShowNotificationIcon, Mode=TwoWay}"
|
||||
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
|
||||
</local:SettingContainer>
|
||||
|
||||
<!-- Minimize To Notification Area -->
|
||||
<local:SettingContainer x:Uid="Globals_MinimizeToNotificationArea">
|
||||
<ToggleSwitch IsOn="{x:Bind State.Globals.MinimizeToNotificationArea, Mode=TwoWay}"
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.MinimizeToNotificationArea, Mode=TwoWay}"
|
||||
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
|
||||
</local:SettingContainer>
|
||||
</StackPanel>
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "GlobalAppearanceViewModel.h"
|
||||
#include "GlobalAppearanceViewModel.g.cpp"
|
||||
#include "EnumEntry.h"
|
||||
|
||||
#include <LibraryResources.h>
|
||||
#include <WtExeUtils.h>
|
||||
|
||||
using namespace winrt;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Xaml::Navigation;
|
||||
using namespace winrt::Windows::UI::Xaml::Controls;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
// For ComboBox an empty SelectedItem string denotes no selection.
|
||||
// What we want instead is for "Use system language" to be selected by default.
|
||||
// --> "und" is synonymous for "Use system language".
|
||||
constexpr std::wstring_view systemLanguageTag{ L"und" };
|
||||
|
||||
static constexpr std::array appLanguageTags{
|
||||
L"en-US",
|
||||
L"de-DE",
|
||||
L"es-ES",
|
||||
L"fr-FR",
|
||||
L"it-IT",
|
||||
L"ja",
|
||||
L"ko",
|
||||
L"pt-BR",
|
||||
L"qps-PLOC",
|
||||
L"qps-PLOCA",
|
||||
L"qps-PLOCM",
|
||||
L"ru",
|
||||
L"zh-Hans",
|
||||
L"zh-Hant",
|
||||
};
|
||||
|
||||
constexpr std::wstring_view systemThemeName{ L"system" };
|
||||
constexpr std::wstring_view darkThemeName{ L"dark" };
|
||||
constexpr std::wstring_view lightThemeName{ L"light" };
|
||||
|
||||
GlobalAppearanceViewModel::GlobalAppearanceViewModel(Model::GlobalAppSettings globalSettings) :
|
||||
_GlobalSettings{ globalSettings },
|
||||
_ThemeList{ single_threaded_observable_vector<Model::Theme>() }
|
||||
{
|
||||
INITIALIZE_BINDABLE_ENUM_SETTING(NewTabPosition, NewTabPosition, NewTabPosition, L"Globals_NewTabPosition", L"Content");
|
||||
INITIALIZE_BINDABLE_ENUM_SETTING(TabWidthMode, TabViewWidthMode, winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, L"Globals_TabWidthMode", L"Content");
|
||||
_UpdateThemeList();
|
||||
}
|
||||
|
||||
winrt::hstring GlobalAppearanceViewModel::LanguageDisplayConverter(const winrt::hstring& tag)
|
||||
{
|
||||
if (tag == systemLanguageTag)
|
||||
{
|
||||
return RS_(L"Globals_LanguageDefault");
|
||||
}
|
||||
|
||||
winrt::Windows::Globalization::Language language{ tag };
|
||||
return language.NativeName();
|
||||
}
|
||||
|
||||
// Returns whether the language selector is available/shown.
|
||||
//
|
||||
// winrt::Windows::Globalization::ApplicationLanguages::PrimaryLanguageOverride()
|
||||
// doesn't work for unpackaged applications. The corresponding code in TerminalApp is disabled.
|
||||
// It would be confusing for our users if we presented a dysfunctional language selector.
|
||||
bool GlobalAppearanceViewModel::LanguageSelectorAvailable()
|
||||
{
|
||||
return IsPackaged();
|
||||
}
|
||||
|
||||
// Returns the list of languages the user may override the application language with.
|
||||
// The returned list are BCP 47 language tags like {"und", "en-US", "de-DE", "es-ES", ...}.
|
||||
// "und" is short for "undefined" and is synonymous for "Use system language" in this code.
|
||||
winrt::Windows::Foundation::Collections::IObservableVector<winrt::hstring> GlobalAppearanceViewModel::LanguageList()
|
||||
{
|
||||
if (_languageList)
|
||||
{
|
||||
return _languageList;
|
||||
}
|
||||
|
||||
if (!LanguageSelectorAvailable())
|
||||
{
|
||||
_languageList = {};
|
||||
return _languageList;
|
||||
}
|
||||
|
||||
// In order to return the language list this code does the following:
|
||||
// [1] Get all possible languages we want to allow the user to choose.
|
||||
// We have to acquire languages from multiple sources, creating duplicates. See below at [1].
|
||||
// [2] Sort languages by their ASCII tags, forcing the UI in a consistent/stable order.
|
||||
// I wanted to sort the localized language names initially, but it turned out to be complex.
|
||||
// [3] Remove potential duplicates in our language list from [1].
|
||||
// We don't want to have en-US twice in the list, do we?
|
||||
// [4] Optionally remove unwanted language tags (like pseudo-localizations).
|
||||
|
||||
std::vector<winrt::hstring> tags;
|
||||
|
||||
// [1]:
|
||||
{
|
||||
// ManifestLanguages contains languages the app ships with.
|
||||
// Unfortunately, we cannot use this source. Our manifest must contain the
|
||||
// ~100 languages that are localized for the shell extension and start menu
|
||||
// presentation so we align with Windows display languages for those surfaces.
|
||||
// However, the actual content of our application is limited to a much smaller
|
||||
// subset of approximately 14 languages. As such, we will code the limited
|
||||
// subset of languages that we support for selection within the Settings
|
||||
// dropdown to steer users towards the ones that we can display in the app.
|
||||
|
||||
// As per the function definition, the first item
|
||||
// is always "Use system language" ("und").
|
||||
tags.emplace_back(systemLanguageTag);
|
||||
|
||||
// Add our hardcoded languages after the system definition.
|
||||
for (const auto& v : appLanguageTags)
|
||||
{
|
||||
tags.push_back(v);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: The size of tags is always >0, due to tags[0] being hardcoded to "und".
|
||||
const auto tagsBegin = ++tags.begin();
|
||||
const auto tagsEnd = tags.end();
|
||||
|
||||
// [2]:
|
||||
std::sort(tagsBegin, tagsEnd);
|
||||
|
||||
// I'd love for both, std::unique and std::remove_if, to occur in a single loop,
|
||||
// but the code turned out to be complex and even less maintainable, so I gave up.
|
||||
{
|
||||
// [3] part 1:
|
||||
auto it = std::unique(tagsBegin, tagsEnd);
|
||||
|
||||
// The qps- languages are useful for testing ("pseudo-localization").
|
||||
// --> Leave them in if debug features are enabled.
|
||||
if (!_GlobalSettings.DebugFeaturesEnabled())
|
||||
{
|
||||
// [4] part 1:
|
||||
it = std::remove_if(tagsBegin, it, [](const winrt::hstring& tag) -> bool {
|
||||
return til::starts_with(tag, L"qps-");
|
||||
});
|
||||
}
|
||||
|
||||
// [3], [4] part 2 (completing the so called "erase-remove idiom"):
|
||||
tags.erase(it, tagsEnd);
|
||||
}
|
||||
|
||||
_languageList = winrt::single_threaded_observable_vector(std::move(tags));
|
||||
return _languageList;
|
||||
}
|
||||
|
||||
winrt::Windows::Foundation::IInspectable GlobalAppearanceViewModel::CurrentLanguage()
|
||||
{
|
||||
if (_currentLanguage)
|
||||
{
|
||||
return _currentLanguage;
|
||||
}
|
||||
|
||||
if (!LanguageSelectorAvailable())
|
||||
{
|
||||
_currentLanguage = {};
|
||||
return _currentLanguage;
|
||||
}
|
||||
|
||||
// NOTE: PrimaryLanguageOverride throws if this instance is unpackaged.
|
||||
auto currentLanguage = winrt::Windows::Globalization::ApplicationLanguages::PrimaryLanguageOverride();
|
||||
if (currentLanguage.empty())
|
||||
{
|
||||
currentLanguage = systemLanguageTag;
|
||||
}
|
||||
|
||||
_currentLanguage = winrt::box_value(currentLanguage);
|
||||
return _currentLanguage;
|
||||
}
|
||||
|
||||
void GlobalAppearanceViewModel::CurrentLanguage(const winrt::Windows::Foundation::IInspectable& tag)
|
||||
{
|
||||
_currentLanguage = tag;
|
||||
|
||||
const auto currentLanguage = winrt::unbox_value<winrt::hstring>(_currentLanguage);
|
||||
if (currentLanguage == systemLanguageTag)
|
||||
{
|
||||
_GlobalSettings.ClearLanguage();
|
||||
}
|
||||
else
|
||||
{
|
||||
_GlobalSettings.Language(currentLanguage);
|
||||
}
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Updates the list of all themes available to choose from.
|
||||
void GlobalAppearanceViewModel::_UpdateThemeList()
|
||||
{
|
||||
// Surprisingly, though this is called every time we navigate to the page,
|
||||
// the list does not keep growing on each navigation.
|
||||
const auto& ThemeMap{ _GlobalSettings.Themes() };
|
||||
for (const auto& pair : ThemeMap)
|
||||
{
|
||||
_ThemeList.Append(pair.Value());
|
||||
}
|
||||
}
|
||||
|
||||
winrt::Windows::Foundation::IInspectable GlobalAppearanceViewModel::CurrentTheme()
|
||||
{
|
||||
return _GlobalSettings.CurrentTheme();
|
||||
}
|
||||
|
||||
// Get the name out of the newly selected item, stash that as the Theme name
|
||||
// set for the globals. That controls which theme is actually the current
|
||||
// theme.
|
||||
void GlobalAppearanceViewModel::CurrentTheme(const winrt::Windows::Foundation::IInspectable& tag)
|
||||
{
|
||||
if (const auto& theme{ tag.try_as<Model::Theme>() })
|
||||
{
|
||||
_GlobalSettings.Theme(theme.Name());
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Convert the names of the inbox themes to some more descriptive,
|
||||
// well-known values. If the passed in theme isn't an inbox one, then just
|
||||
// return its set Name.
|
||||
// - "light" becomes "Light"
|
||||
// - "dark" becomes "Dark"
|
||||
// - "system" becomes "Use Windows theme"
|
||||
// - These values are all localized based on the app language.
|
||||
// Arguments:
|
||||
// - theme: the theme to get the display name for.
|
||||
// Return Value:
|
||||
// - the potentially localized name to use for this Theme.
|
||||
winrt::hstring GlobalAppearanceViewModel::ThemeNameConverter(const Model::Theme& theme)
|
||||
{
|
||||
if (theme.Name() == darkThemeName)
|
||||
{
|
||||
return RS_(L"Globals_ThemeDark/Content");
|
||||
}
|
||||
else if (theme.Name() == lightThemeName)
|
||||
{
|
||||
return RS_(L"Globals_ThemeLight/Content");
|
||||
}
|
||||
else if (theme.Name() == systemThemeName)
|
||||
{
|
||||
return RS_(L"Globals_ThemeSystem/Content");
|
||||
}
|
||||
return theme.Name();
|
||||
}
|
||||
|
||||
bool GlobalAppearanceViewModel::InvertedDisableAnimations()
|
||||
{
|
||||
return !_GlobalSettings.DisableAnimations();
|
||||
}
|
||||
|
||||
void GlobalAppearanceViewModel::InvertedDisableAnimations(bool value)
|
||||
{
|
||||
_GlobalSettings.DisableAnimations(!value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GlobalAppearanceViewModel.g.h"
|
||||
#include "ViewModelHelpers.h"
|
||||
#include "Utils.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
struct GlobalAppearanceViewModel : GlobalAppearanceViewModelT<GlobalAppearanceViewModel>, ViewModelHelper<GlobalAppearanceViewModel>
|
||||
{
|
||||
public:
|
||||
GlobalAppearanceViewModel(Model::GlobalAppSettings globalSettings);
|
||||
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
WINRT_PROPERTY(Windows::Foundation::Collections::IObservableVector<Model::Theme>, ThemeList, nullptr);
|
||||
GETSET_BINDABLE_ENUM_SETTING(NewTabPosition, Model::NewTabPosition, _GlobalSettings.NewTabPosition);
|
||||
GETSET_BINDABLE_ENUM_SETTING(TabWidthMode, winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, _GlobalSettings.TabWidthMode);
|
||||
|
||||
public:
|
||||
// LanguageDisplayConverter maps the given BCP 47 tag to a localized string.
|
||||
// For instance "en-US" produces "English (United States)", while "de-DE" produces
|
||||
// "Deutsch (Deutschland)". This works independently of the user's locale.
|
||||
static winrt::hstring LanguageDisplayConverter(const winrt::hstring& tag);
|
||||
|
||||
bool LanguageSelectorAvailable();
|
||||
winrt::Windows::Foundation::Collections::IObservableVector<winrt::hstring> LanguageList();
|
||||
winrt::Windows::Foundation::IInspectable CurrentLanguage();
|
||||
void CurrentLanguage(const winrt::Windows::Foundation::IInspectable& tag);
|
||||
|
||||
winrt::Windows::Foundation::IInspectable CurrentTheme();
|
||||
void CurrentTheme(const winrt::Windows::Foundation::IInspectable& tag);
|
||||
static winrt::hstring ThemeNameConverter(const Model::Theme& theme);
|
||||
|
||||
bool InvertedDisableAnimations();
|
||||
void InvertedDisableAnimations(bool value);
|
||||
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, AlwaysShowTabs);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, ShowTabsInTitlebar);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, UseAcrylicInTabRow);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, ShowTitleInTitlebar);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, AlwaysOnTop);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, AlwaysShowNotificationIcon);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, MinimizeToNotificationArea);
|
||||
|
||||
private:
|
||||
Model::GlobalAppSettings _GlobalSettings;
|
||||
winrt::Windows::Foundation::Collections::IObservableVector<winrt::hstring> _languageList;
|
||||
winrt::Windows::Foundation::IInspectable _currentLanguage;
|
||||
winrt::Windows::Foundation::IInspectable _currentTheme;
|
||||
|
||||
void _UpdateThemeList();
|
||||
};
|
||||
};
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(GlobalAppearanceViewModel);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import "EnumEntry.idl";
|
||||
|
||||
#include "ViewModelHelpers.idl.h"
|
||||
|
||||
namespace Microsoft.Terminal.Settings.Editor
|
||||
{
|
||||
runtimeclass GlobalAppearanceViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
GlobalAppearanceViewModel(Microsoft.Terminal.Settings.Model.GlobalAppSettings globalSettings);
|
||||
|
||||
static String LanguageDisplayConverter(String tag);
|
||||
Boolean LanguageSelectorAvailable { get; };
|
||||
Windows.Foundation.Collections.IObservableVector<String> LanguageList { get; };
|
||||
IInspectable CurrentLanguage;
|
||||
|
||||
IInspectable CurrentTheme;
|
||||
static String ThemeNameConverter(Microsoft.Terminal.Settings.Model.Theme theme);
|
||||
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Model.Theme> ThemeList { get; };
|
||||
|
||||
IInspectable CurrentNewTabPosition;
|
||||
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> NewTabPositionList { get; };
|
||||
|
||||
IInspectable CurrentTabWidthMode;
|
||||
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> TabWidthModeList { get; };
|
||||
|
||||
Boolean InvertedDisableAnimations;
|
||||
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, AlwaysShowTabs);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, ShowTabsInTitlebar);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, UseAcrylicInTabRow);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, ShowTitleInTitlebar);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, AlwaysOnTop);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, AlwaysShowNotificationIcon);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, MinimizeToNotificationArea);
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,6 @@
|
||||
#include "pch.h"
|
||||
#include "Interaction.h"
|
||||
#include "Interaction.g.cpp"
|
||||
#include "InteractionPageNavigationState.g.cpp"
|
||||
#include "EnumEntry.h"
|
||||
|
||||
using namespace winrt::Windows::UI::Xaml::Navigation;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
@@ -15,13 +13,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
Interaction::Interaction()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
INITIALIZE_BINDABLE_ENUM_SETTING(TabSwitcherMode, TabSwitcherMode, TabSwitcherMode, L"Globals_TabSwitcherMode", L"Content");
|
||||
INITIALIZE_BINDABLE_ENUM_SETTING(CopyFormat, CopyFormat, winrt::Microsoft::Terminal::Control::CopyFormat, L"Globals_CopyFormat", L"Content");
|
||||
}
|
||||
|
||||
void Interaction::OnNavigatedTo(const NavigationEventArgs& e)
|
||||
{
|
||||
_State = e.Parameter().as<Editor::InteractionPageNavigationState>();
|
||||
_ViewModel = e.Parameter().as<Editor::InteractionViewModel>();
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user