mirror of
https://github.com/microsoft/terminal.git
synced 2026-05-20 05:54:23 +00:00
Compare commits
70 Commits
dev/cazamo
...
v1.23.1175
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db9774c17e | ||
|
|
38f28e80d0 | ||
|
|
820b10fd2b | ||
|
|
2801e9af50 | ||
|
|
dcdc21381c | ||
|
|
dd2ebc5c09 | ||
|
|
dc3bb80c16 | ||
|
|
9b80830d56 | ||
|
|
161a6b8784 | ||
|
|
aab0fa00ab | ||
|
|
8905ac1164 | ||
|
|
94a2aa0100 | ||
|
|
b6f56d63f5 | ||
|
|
90a55ed3d9 | ||
|
|
9476d0f9fa | ||
|
|
c5a5064d61 | ||
|
|
412083818d | ||
|
|
a8251a913b | ||
|
|
6027eca9f3 | ||
|
|
2f29dc8f62 | ||
|
|
3c59c3c050 | ||
|
|
c2f9191fc3 | ||
|
|
5ddbb9897b | ||
|
|
a36598a3b5 | ||
|
|
04a613c9ee | ||
|
|
4aa4d4a989 | ||
|
|
24a35cedcf | ||
|
|
b29a985f54 | ||
|
|
684b91f5f1 | ||
|
|
f3527bedf2 | ||
|
|
e8a883fd41 | ||
|
|
42a0b133d3 | ||
|
|
89cb70f098 | ||
|
|
c37b848845 | ||
|
|
8771b985ae | ||
|
|
ca218d3d7a | ||
|
|
b2cf9d1bac | ||
|
|
840f9623e5 | ||
|
|
cf3bbf53e6 | ||
|
|
3ff08aea1f | ||
|
|
16b737f9f5 | ||
|
|
aceb042499 | ||
|
|
3e70851d82 | ||
|
|
97c6ecb5a6 | ||
|
|
17ea33c42e | ||
|
|
21e63adec5 | ||
|
|
a9db4e403f | ||
|
|
d20c217751 | ||
|
|
7600888118 | ||
|
|
4f8a3d1845 | ||
|
|
38783fa595 | ||
|
|
a0140ef644 | ||
|
|
490e2bfc06 | ||
|
|
c1cd6d3d1d | ||
|
|
d938f924bb | ||
|
|
e9520c02ea | ||
|
|
2060fd6b11 | ||
|
|
70996f35d0 | ||
|
|
a37dc26fd8 | ||
|
|
29e401f202 | ||
|
|
0d7e1293fa | ||
|
|
a72531014c | ||
|
|
2590ff1383 | ||
|
|
863cdd44f2 | ||
|
|
73721c7a90 | ||
|
|
deeba28fda | ||
|
|
b43e7b93ec | ||
|
|
c6e20e99d7 | ||
|
|
8b0fc20f83 | ||
|
|
77638840e4 |
6
.github/actions/spelling/allow/allow.txt
vendored
6
.github/actions/spelling/allow/allow.txt
vendored
@@ -11,6 +11,7 @@ colorbrewer
|
||||
commandlines
|
||||
consvc
|
||||
copyable
|
||||
CText
|
||||
dalet
|
||||
dcs
|
||||
deselection
|
||||
@@ -22,8 +23,8 @@ dze
|
||||
dzhe
|
||||
Emacspeak
|
||||
Fitt
|
||||
FTCS
|
||||
flac
|
||||
FTCS
|
||||
gantt
|
||||
gfm
|
||||
ghe
|
||||
@@ -59,8 +60,8 @@ Powerline
|
||||
ptys
|
||||
pwn
|
||||
pwshw
|
||||
QOL
|
||||
qof
|
||||
QOL
|
||||
qps
|
||||
quickfix
|
||||
rclt
|
||||
@@ -80,6 +81,7 @@ stakeholders
|
||||
subpage
|
||||
sustainability
|
||||
sxn
|
||||
Tencent
|
||||
TLDR
|
||||
tonos
|
||||
toolset
|
||||
|
||||
2
.github/actions/spelling/allow/apis.txt
vendored
2
.github/actions/spelling/allow/apis.txt
vendored
@@ -144,6 +144,7 @@ NCHITTEST
|
||||
NCLBUTTONDBLCLK
|
||||
NCMOUSELEAVE
|
||||
NCMOUSEMOVE
|
||||
NCPOINTERUPDATE
|
||||
NCRBUTTONDBLCLK
|
||||
NIF
|
||||
NIN
|
||||
@@ -289,4 +290,5 @@ xtree
|
||||
xutility
|
||||
YIcon
|
||||
YMax
|
||||
zstring
|
||||
zwstring
|
||||
|
||||
4
.github/actions/spelling/expect/expect.txt
vendored
4
.github/actions/spelling/expect/expect.txt
vendored
@@ -86,6 +86,7 @@ autoscrolling
|
||||
Autowrap
|
||||
AVerify
|
||||
awch
|
||||
AZCOPY
|
||||
azurecr
|
||||
AZZ
|
||||
backgrounded
|
||||
@@ -215,6 +216,7 @@ codepages
|
||||
codepath
|
||||
coinit
|
||||
colorizing
|
||||
COLORONCOLOR
|
||||
COLORREFs
|
||||
colorschemes
|
||||
colorspec
|
||||
@@ -1415,6 +1417,7 @@ propvar
|
||||
propvariant
|
||||
propvarutil
|
||||
psa
|
||||
PSCRED
|
||||
PSECURITY
|
||||
pseudoconsole
|
||||
pseudoterminal
|
||||
@@ -1676,6 +1679,7 @@ SMARTQUOTE
|
||||
SMTO
|
||||
snapcx
|
||||
snapcy
|
||||
snk
|
||||
SOLIDBOX
|
||||
Solutiondir
|
||||
somefile
|
||||
|
||||
BIN
build/config/272MSSharedLibSN2048.snk
Normal file
BIN
build/config/272MSSharedLibSN2048.snk
Normal file
Binary file not shown.
@@ -6,6 +6,20 @@
|
||||
],
|
||||
"SigningInfo": {
|
||||
"Operations": [
|
||||
{
|
||||
"KeyCode": "CP-233904-SN",
|
||||
"OperationSetCode": "StrongNameSign",
|
||||
"ToolName": "sign",
|
||||
"ToolVersion": "1.0",
|
||||
"Parameters": []
|
||||
},
|
||||
{
|
||||
"KeyCode": "CP-233904-SN",
|
||||
"OperationSetCode": "StrongNameVerify",
|
||||
"ToolName": "sign",
|
||||
"ToolVersion": "1.0",
|
||||
"Parameters": []
|
||||
},
|
||||
{
|
||||
"KeyCode": "CP-230012",
|
||||
"OperationSetCode": "SigntoolSign",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"instanceUrl": "https://microsoft.visualstudio.com",
|
||||
"projectName": "OS",
|
||||
"areaPath": "OS\\Windows Client and Services\\WinPD\\DEEP-Developer Experience, Ecosystem and Partnerships\\SHINE\\Terminal",
|
||||
"areaPath": "OS\\Windows Client and Services\\WinPD\\DFX-Developer Fundamentals and Experiences\\DEFT\\SHINE\\Terminal",
|
||||
"notificationAliases": ["condev@microsoft.com", "duhowett@microsoft.com"]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="MUXCustomBuildTasks" version="1.0.48" targetFramework="native" />
|
||||
<package id="Microsoft.Taef" version="10.93.240607003" targetFramework="native" />
|
||||
<package id="Microsoft.Internal.PGO-Helpers.Cpp" version="0.2.34" targetFramework="native" />
|
||||
<package id="Microsoft.Debugging.Tools.PdbStr" version="20220617.1556.0" targetFramework="native" />
|
||||
|
||||
@@ -75,18 +75,13 @@ jobs:
|
||||
}
|
||||
displayName: "Wrangle Unpackaged builds into place, rename"
|
||||
|
||||
- powershell: |-
|
||||
Get-PackageProvider -Name NuGet -ForceBootstrap
|
||||
Install-Module -Verbose -AllowClobber -Force Az.Accounts, Az.Storage, Az.Network, Az.Resources, Az.Compute
|
||||
displayName: Install Azure Module Dependencies
|
||||
|
||||
- task: AzureFileCopy@6
|
||||
- task: AzurePowerShell@5
|
||||
displayName: Publish to Storage Account
|
||||
inputs:
|
||||
sourcePath: _out/*
|
||||
Destination: AzureBlob
|
||||
azureSubscription: ${{ parameters.subscription }}
|
||||
storage: ${{ parameters.storageAccount }}
|
||||
ContainerName: ${{ parameters.storageContainer }}
|
||||
AdditionalArgumentsForBlobCopy: "--content-type application/octet-stream"
|
||||
|
||||
azurePowerShellVersion: LatestVersion
|
||||
pwsh: true
|
||||
ScriptType: InlineScript
|
||||
Inline: |-
|
||||
$Env:AZCOPY_AUTO_LOGIN_TYPE="PSCRED"
|
||||
& AzCopy copy "_out\*" "https://${{ parameters.storageAccount }}.blob.core.windows.net/${{ parameters.storageContainer }}/" --content-type application/octet-stream
|
||||
|
||||
@@ -147,6 +147,10 @@ jobs:
|
||||
ValidateSignature: true
|
||||
Verbosity: 'Verbose'
|
||||
|
||||
- pwsh: |-
|
||||
tar -c -v --format=zip -f "$(JobOutputDirectory)/GroupPolicyTemplates_$(XES_APPXMANIFESTVERSION).zip" -C "$(Build.SourcesDirectory)/policies" *
|
||||
displayName: Package GPO Templates
|
||||
|
||||
- ${{ parameters.afterBuildSteps }}
|
||||
|
||||
- ${{ if eq(parameters.publishArtifacts, true) }}:
|
||||
|
||||
@@ -52,11 +52,6 @@ jobs:
|
||||
itemPattern: '**/*.pdb'
|
||||
targetPath: '$(Build.SourcesDirectory)/bin'
|
||||
|
||||
- powershell: |-
|
||||
Get-PackageProvider -Name NuGet -ForceBootstrap
|
||||
Install-Module -Verbose -AllowClobber -Force Az.Accounts, Az.Storage, Az.Network, Az.Resources, Az.Compute
|
||||
displayName: Install Azure Module Dependencies
|
||||
|
||||
# Transit the Azure token from the Service Connection into a secret variable for the rest of the pipeline to use.
|
||||
- task: AzurePowerShell@5
|
||||
displayName: Generate an Azure Token
|
||||
@@ -66,7 +61,7 @@ jobs:
|
||||
pwsh: true
|
||||
ScriptType: InlineScript
|
||||
Inline: |-
|
||||
$AzToken = (Get-AzAccessToken -ResourceUrl api://30471ccf-0966-45b9-a979-065dbedb24c1).Token
|
||||
$AzToken = (Get-AzAccessToken -AsSecureString -ResourceUrl api://30471ccf-0966-45b9-a979-065dbedb24c1).Token | ConvertFrom-SecureString -AsPlainText
|
||||
Write-Host "##vso[task.setvariable variable=SymbolAccessToken;issecret=true]$AzToken"
|
||||
|
||||
|
||||
|
||||
@@ -78,7 +78,9 @@ extends:
|
||||
template: v2/Microsoft.NonOfficial.yml@templates
|
||||
parameters:
|
||||
featureFlags:
|
||||
WindowsHostVersion: 1ESWindows2022
|
||||
WindowsHostVersion:
|
||||
Version: 2022
|
||||
Network: R1
|
||||
platform:
|
||||
name: 'windows_undocked'
|
||||
product: 'Windows Terminal'
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
steps:
|
||||
- task: NuGetToolInstaller@1
|
||||
displayName: Use NuGet 6.6.1
|
||||
inputs:
|
||||
versionSpec: 6.6.1
|
||||
- ${{ if eq(variables['System.CollectionId'], 'cb55739e-4afe-46a3-970f-1b49d8ee7564') }}:
|
||||
- pwsh: |-
|
||||
Write-Host "Assuming NuGet is already installed..."
|
||||
& nuget.exe help
|
||||
displayName: Assume NuGet is fine
|
||||
|
||||
- ${{ else }}:
|
||||
- task: NuGetToolInstaller@1
|
||||
displayName: Use NuGet 6.6.1
|
||||
inputs:
|
||||
versionSpec: 6.6.1
|
||||
|
||||
@@ -1,8 +1,28 @@
|
||||
$VSInstances = ([xml](& 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -include packages -format xml))
|
||||
$VSPackages = $VSInstances.instances.instance.packages.package
|
||||
$LatestVCPackage = ($VSInstances.instances.instance.packages.package | ? { $_.id -eq "Microsoft.VisualCpp.Tools.Core" })
|
||||
$LatestVCPackage = ($VSPackages | ? { $_.id -eq "Microsoft.VisualCpp.Tools.Core" })
|
||||
$LatestVCToolsVersion = $LatestVCPackage.version;
|
||||
|
||||
$VSRoot = (& 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property 'resolvedInstallationPath')
|
||||
$VCToolsRoot = Join-Path $VSRoot "VC\Tools\MSVC"
|
||||
|
||||
# We have observed a few instances where the VC tools package version actually
|
||||
# differs from the version on the files themselves. We might as well check
|
||||
# whether the version we just found _actually exists_ before we use it.
|
||||
# We'll use whichever highest version exists.
|
||||
$PackageVCToolPath = Join-Path $VCToolsRoot $LatestVCToolsVersion
|
||||
If ($Null -Eq (Get-Item $PackageVCToolPath -ErrorAction:Ignore)) {
|
||||
$VCToolsVersions = Get-ChildItem $VCToolsRoot | ForEach-Object {
|
||||
[Version]$_.Name
|
||||
} | Sort -Descending
|
||||
$LatestActualVCToolsVersion = $VCToolsVersions | Select -First 1
|
||||
|
||||
If ([Version]$LatestVCToolsVersion -Ne $LatestActualVCToolsVersion) {
|
||||
Write-Output "VC Tools Mismatch: Directory = $LatestActualVCToolsVersion, Package = $LatestVCToolsVersion"
|
||||
$LatestVCToolsVersion = $LatestActualVCToolsVersion.ToString(3)
|
||||
}
|
||||
}
|
||||
|
||||
Write-Output "Latest VCToolsVersion: $LatestVCToolsVersion"
|
||||
Write-Output "Updating VCToolsVersion environment variable for job"
|
||||
Write-Output "##vso[task.setvariable variable=VCToolsVersion]$LatestVCToolsVersion"
|
||||
|
||||
13
dep/vcpkg-overlay-ports/fmt/fix-write-batch.patch
Normal file
13
dep/vcpkg-overlay-ports/fmt/fix-write-batch.patch
Normal file
@@ -0,0 +1,13 @@
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index 88c12148..967b53dd 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -260,7 +260,7 @@ if (FMT_MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio")
|
||||
join(netfxpath
|
||||
"C:\\Program Files\\Reference Assemblies\\Microsoft\\Framework\\"
|
||||
".NETFramework\\v4.0")
|
||||
- file(WRITE run-msbuild.bat "
|
||||
+ file(WRITE "${CMAKE_BINARY_DIR}/run-msbuild.bat" "
|
||||
${MSBUILD_SETUP}
|
||||
${CMAKE_MAKE_PROGRAM} -p:FrameworkPathOverride=\"${netfxpath}\" %*")
|
||||
endif ()
|
||||
38
dep/vcpkg-overlay-ports/fmt/portfile.cmake
Normal file
38
dep/vcpkg-overlay-ports/fmt/portfile.cmake
Normal file
@@ -0,0 +1,38 @@
|
||||
vcpkg_from_github(
|
||||
OUT_SOURCE_PATH SOURCE_PATH
|
||||
REPO fmtlib/fmt
|
||||
REF "${VERSION}"
|
||||
SHA512 573b7de1bd224b7b1b60d44808a843db35d4bc4634f72a9edcb52cf68e99ca66c744fd5d5c97b4336ba70b94abdabac5fc253b245d0d5cd8bbe2a096bf941e39
|
||||
HEAD_REF master
|
||||
PATCHES
|
||||
fix-write-batch.patch
|
||||
)
|
||||
|
||||
vcpkg_cmake_configure(
|
||||
SOURCE_PATH "${SOURCE_PATH}"
|
||||
OPTIONS
|
||||
-DFMT_CMAKE_DIR=share/fmt
|
||||
-DFMT_TEST=OFF
|
||||
-DFMT_DOC=OFF
|
||||
-DFMT_PEDANTIC=ON
|
||||
)
|
||||
|
||||
vcpkg_cmake_install()
|
||||
vcpkg_cmake_config_fixup()
|
||||
vcpkg_fixup_pkgconfig()
|
||||
vcpkg_copy_pdbs()
|
||||
|
||||
if(VCPKG_LIBRARY_LINKAGE STREQUAL dynamic)
|
||||
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/include/fmt/base.h"
|
||||
"defined(FMT_SHARED)"
|
||||
"1"
|
||||
)
|
||||
endif()
|
||||
|
||||
file(REMOVE_RECURSE
|
||||
"${CURRENT_PACKAGES_DIR}/debug/include"
|
||||
"${CURRENT_PACKAGES_DIR}/debug/share"
|
||||
)
|
||||
|
||||
file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}")
|
||||
vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE")
|
||||
8
dep/vcpkg-overlay-ports/fmt/usage
Normal file
8
dep/vcpkg-overlay-ports/fmt/usage
Normal file
@@ -0,0 +1,8 @@
|
||||
The package fmt provides CMake targets:
|
||||
|
||||
find_package(fmt CONFIG REQUIRED)
|
||||
target_link_libraries(main PRIVATE fmt::fmt)
|
||||
|
||||
# Or use the header-only version
|
||||
find_package(fmt CONFIG REQUIRED)
|
||||
target_link_libraries(main PRIVATE fmt::fmt-header-only)
|
||||
17
dep/vcpkg-overlay-ports/fmt/vcpkg.json
Normal file
17
dep/vcpkg-overlay-ports/fmt/vcpkg.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "fmt",
|
||||
"version": "11.1.4",
|
||||
"description": "{fmt} is an open-source formatting library providing a fast and safe alternative to C stdio and C++ iostreams.",
|
||||
"homepage": "https://github.com/fmtlib/fmt",
|
||||
"license": "MIT",
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "vcpkg-cmake",
|
||||
"host": true
|
||||
},
|
||||
{
|
||||
"name": "vcpkg-cmake-config",
|
||||
"host": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -2480,11 +2480,6 @@
|
||||
"minimum": 1,
|
||||
"type": "integer"
|
||||
},
|
||||
"startOnUserLogin": {
|
||||
"default": false,
|
||||
"description": "When set to true, this enables the launch of Terminal at startup. Setting this to false will disable the startup task entry. If the Terminal startup task entry is disabled either by org policy or by user action this setting will have no effect.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"firstWindowPreference": {
|
||||
"default": "defaultProfile",
|
||||
"description": "Defines what behavior the terminal takes when it starts. \"defaultProfile\" will have the terminal launch with one tab of the default profile, and \"persistedWindowLayout\" will cause the terminal to save its layout on close and reload it on open.",
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<supportedOn>
|
||||
<definitions>
|
||||
<definition name="SUPPORTED_WindowsTerminal_1_21" displayName="$(string.SUPPORTED_WindowsTerminal_1_21)" />
|
||||
<definition name="SUPPORTED_DefaultTerminalApplication" displayName="$(string.SUPPORTED_DefaultTerminalApplication)" />
|
||||
</definitions>
|
||||
</supportedOn>
|
||||
<categories>
|
||||
@@ -24,5 +25,61 @@
|
||||
<multiText id="DisabledProfileSources" valueName="DisabledProfileSources" required="true" />
|
||||
</elements>
|
||||
</policy>
|
||||
<policy name="DefaultTerminalApplication" class="User" displayName="$(string.DefaultTerminalApplication)" explainText="$(string.DefaultTerminalApplicationText)" presentation="$(presentation.TermAppSelection)" key="Console\%%Startup">
|
||||
<parentCategory ref="WindowsTerminal" />
|
||||
<supportedOn ref="SUPPORTED_DefaultTerminalApplication" />
|
||||
<elements>
|
||||
<enum id="TermAppSelect" required="true" valueName="DelegationTerminal">
|
||||
<item displayName="$(string.TermAppAutomatic)">
|
||||
<value>
|
||||
<string>{00000000-0000-0000-0000-000000000000}</string>
|
||||
</value>
|
||||
<valueList>
|
||||
<item key="Console\%%Startup" valueName="DelegationConsole">
|
||||
<value>
|
||||
<string>{00000000-0000-0000-0000-000000000000}</string>
|
||||
</value>
|
||||
</item>
|
||||
</valueList>
|
||||
</item>
|
||||
<item displayName="$(string.TermAppConsoleHost)">
|
||||
<value>
|
||||
<string>{B23D10C0-E52E-411E-9D5B-C09FDF709C7D}</string>
|
||||
</value>
|
||||
<valueList>
|
||||
<item key="Console\%%Startup" valueName="DelegationConsole">
|
||||
<value>
|
||||
<string>{B23D10C0-E52E-411E-9D5B-C09FDF709C7D}</string>
|
||||
</value>
|
||||
</item>
|
||||
</valueList>
|
||||
</item>
|
||||
<item displayName="$(string.TermAppWindowsTerminal)">
|
||||
<value>
|
||||
<string>{E12CFF52-A866-4C77-9A90-F570A7AA2C6B}</string>
|
||||
</value>
|
||||
<valueList>
|
||||
<item key="Console\%%Startup" valueName="DelegationConsole">
|
||||
<value>
|
||||
<string>{2EACA947-7F5F-4CFA-BA87-8F7FBEEFBE69}</string>
|
||||
</value>
|
||||
</item>
|
||||
</valueList>
|
||||
</item>
|
||||
<item displayName="$(string.TermAppWindowsTerminalPreview)">
|
||||
<value>
|
||||
<string>{86633F1F-6454-40EC-89CE-DA4EBA977EE2}</string>
|
||||
</value>
|
||||
<valueList>
|
||||
<item key="Console\%%Startup" valueName="DelegationConsole">
|
||||
<value>
|
||||
<string>{06EC847C-C0A5-46B8-92CB-7C92F6E35CD5}</string>
|
||||
</value>
|
||||
</item>
|
||||
</valueList>
|
||||
</item>
|
||||
</enum>
|
||||
</elements>
|
||||
</policy>
|
||||
</policies>
|
||||
</policyDefinitions>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<stringTable>
|
||||
<string id="WindowsTerminal">Windows Terminal</string>
|
||||
<string id="SUPPORTED_WindowsTerminal_1_21">At least Windows Terminal 1.21</string>
|
||||
<string id="SUPPORTED_DefaultTerminalApplication">At least Windows 11 22H2 or Windows 10 22H2 (Build 19045.3031, KB5026435) with Windows Terminal 1.17</string>
|
||||
<string id="DisabledProfileSources">Disabled Profile Sources</string>
|
||||
<string id="DisabledProfileSourcesText">Profiles will not be generated from any sources listed here. Source names can be arbitrary strings. Potential candidates can be found as the "source" property on profile definitions in Windows Terminal's settings.json file.
|
||||
|
||||
@@ -18,11 +19,22 @@ Common sources are:
|
||||
For instance, setting this policy to Windows.Terminal.Wsl will disable the builtin WSL integration of Windows Terminal.
|
||||
|
||||
Note: Existing profiles will disappear from Windows Terminal after adding their source to this policy.</string>
|
||||
<string id="DefaultTerminalApplication">Default terminal application</string>
|
||||
<string id="DefaultTerminalApplicationText">Select the default terminal application used in Windows.
|
||||
|
||||
If you select Windows Terminal Preview and it is not installed the system will fallback to the legacy Windows Console Host. (Please note that the settings interfaces showing "Let windows decide" in this case as configuration.)</string>
|
||||
<string id="TermAppAutomatic">Automatic selection (Windows Terminal, if available)</string>
|
||||
<string id="TermAppConsoleHost">Windows Console Host (legacy)</string>
|
||||
<string id="TermAppWindowsTerminal">Windows Terminal</string>
|
||||
<string id="TermAppWindowsTerminalPreview">Windows Terminal Preview (if available)</string>
|
||||
</stringTable>
|
||||
<presentationTable>
|
||||
<presentation id="DisabledProfileSources">
|
||||
<multiTextBox refId="DisabledProfileSources">List of disabled sources (one per line)</multiTextBox>
|
||||
</presentation>
|
||||
<presentation id="TermAppSelection">
|
||||
<dropdownList refId="TermAppSelect" noSort="true" defaultItem="0">Select from the following options:</dropdownList>
|
||||
</presentation>
|
||||
</presentationTable>
|
||||
</resources>
|
||||
</policyDefinitionResources>
|
||||
|
||||
@@ -186,18 +186,20 @@ bool ImageSlice::_copyCells(const ImageSlice& srcSlice, const til::CoordType src
|
||||
}
|
||||
|
||||
// The used destination before and after the written area must be erased.
|
||||
if (dstUsedBegin < dstWriteBegin)
|
||||
// If this results in the entire range being erased, we return true to let
|
||||
// the caller know that the slice should be deleted.
|
||||
if (dstUsedBegin < dstWriteBegin && _eraseCells(dstUsedBegin, dstWriteBegin))
|
||||
{
|
||||
_eraseCells(dstUsedBegin, dstWriteBegin);
|
||||
return true;
|
||||
}
|
||||
if (dstUsedEnd > dstWriteEnd)
|
||||
if (dstUsedEnd > dstWriteEnd && _eraseCells(dstWriteEnd, dstUsedEnd))
|
||||
{
|
||||
_eraseCells(dstWriteEnd, dstUsedEnd);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the beginning column is now not less than the end, that means the
|
||||
// content has been entirely erased, so we return true to let the caller
|
||||
// know that the slice should be deleted.
|
||||
// At this point, if the beginning column is not less than the end, that
|
||||
// means this was an empty slice into which nothing was copied, so we can
|
||||
// again return true to let the caller know it should be deleted.
|
||||
return _columnBegin >= _columnEnd;
|
||||
}
|
||||
|
||||
@@ -210,10 +212,19 @@ void ImageSlice::EraseBlock(TextBuffer& buffer, const til::rect rect)
|
||||
}
|
||||
}
|
||||
|
||||
void ImageSlice::EraseCells(TextBuffer& buffer, const til::point at, const size_t distance)
|
||||
void ImageSlice::EraseCells(TextBuffer& buffer, const til::point at, const til::CoordType distance)
|
||||
{
|
||||
auto& row = buffer.GetMutableRowByOffset(at.y);
|
||||
EraseCells(row, at.x, gsl::narrow_cast<til::CoordType>(at.x + distance));
|
||||
auto x = at.x;
|
||||
auto y = at.y;
|
||||
auto distanceRemaining = distance;
|
||||
while (distanceRemaining > 0)
|
||||
{
|
||||
auto& row = buffer.GetMutableRowByOffset(y);
|
||||
EraseCells(row, x, x + distanceRemaining);
|
||||
distanceRemaining -= (static_cast<til::CoordType>(row.size()) - x);
|
||||
x = 0;
|
||||
y++;
|
||||
}
|
||||
}
|
||||
|
||||
void ImageSlice::EraseCells(ROW& row, const til::CoordType columnBegin, const til::CoordType columnEnd)
|
||||
|
||||
@@ -41,7 +41,7 @@ public:
|
||||
static void CopyRow(const ROW& srcRow, ROW& dstRow);
|
||||
static void CopyCells(const ROW& srcRow, const til::CoordType srcColumn, ROW& dstRow, const til::CoordType dstColumnBegin, const til::CoordType dstColumnEnd);
|
||||
static void EraseBlock(TextBuffer& buffer, const til::rect rect);
|
||||
static void EraseCells(TextBuffer& buffer, const til::point at, const size_t distance);
|
||||
static void EraseCells(TextBuffer& buffer, const til::point at, const til::CoordType distance);
|
||||
static void EraseCells(ROW& row, const til::CoordType columnBegin, const til::CoordType columnEnd);
|
||||
|
||||
private:
|
||||
|
||||
@@ -431,7 +431,7 @@ OutputCellIterator ROW::WriteCells(OutputCellIterator it, const til::CoordType c
|
||||
THROW_HR_IF(E_INVALIDARG, limitRight.value_or(0) >= size());
|
||||
|
||||
// If we're given a right-side column limit, use it. Otherwise, the write limit is the final column index available in the char row.
|
||||
const auto finalColumnInRow = limitRight.value_or(size() - 1);
|
||||
const auto finalColumnInRow = gsl::narrow_cast<uint16_t>(limitRight.value_or(size() - 1));
|
||||
|
||||
auto currentColor = it->TextAttr();
|
||||
uint16_t colorUses = 0;
|
||||
|
||||
@@ -232,6 +232,11 @@ void TextAttribute::SetRightVerticalDisplayed(const bool isDisplayed) noexcept
|
||||
WI_UpdateFlag(_attrs, CharacterAttributes::RightGridline, isDisplayed);
|
||||
}
|
||||
|
||||
bool TextAttribute::IsBold(const bool intenseIsBold) const noexcept
|
||||
{
|
||||
return IsIntense() && (intenseIsBold || !_foreground.CanBeBrightened());
|
||||
}
|
||||
|
||||
bool TextAttribute::IsIntense() const noexcept
|
||||
{
|
||||
return WI_IsFlagSet(_attrs, CharacterAttributes::Intense);
|
||||
|
||||
@@ -115,6 +115,7 @@ public:
|
||||
return memcmp(this, &other, sizeof(TextAttribute)) != 0;
|
||||
}
|
||||
|
||||
bool IsBold(const bool intenseIsBold) const noexcept;
|
||||
bool IsLegacy() const noexcept;
|
||||
bool IsIntense() const noexcept;
|
||||
bool IsFaint() const noexcept;
|
||||
|
||||
@@ -2061,14 +2061,6 @@ void TextBuffer::_ExpandTextRow(til::inclusive_rect& textRow) const
|
||||
}
|
||||
}
|
||||
|
||||
size_t TextBuffer::SpanLength(const til::point coordStart, const til::point coordEnd) const
|
||||
{
|
||||
const auto bufferSize = GetSize();
|
||||
// The coords are inclusive, so to get the (inclusive) length we add 1.
|
||||
const auto length = bufferSize.CompareInBounds(coordEnd, coordStart) + 1;
|
||||
return gsl::narrow<size_t>(length);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Retrieves the plain text data between the specified coordinates.
|
||||
// Arguments:
|
||||
@@ -2286,7 +2278,7 @@ std::string TextBuffer::GenHTML(const CopyRequest& req,
|
||||
fmt::format_to(std::back_inserter(htmlBuilder), FMT_COMPILE("color:{};"), fgHex);
|
||||
fmt::format_to(std::back_inserter(htmlBuilder), FMT_COMPILE("background-color:{};"), bgHex);
|
||||
|
||||
if (isIntenseBold && attr.IsIntense())
|
||||
if (attr.IsBold(isIntenseBold))
|
||||
{
|
||||
htmlBuilder += "font-weight:bold;";
|
||||
}
|
||||
@@ -2536,7 +2528,7 @@ std::string TextBuffer::GenRTF(const CopyRequest& req,
|
||||
fmt::format_to(std::back_inserter(contentBuilder), FMT_COMPILE("\\cf{}"), fgIdx);
|
||||
fmt::format_to(std::back_inserter(contentBuilder), FMT_COMPILE("\\chshdng0\\chcbpat{}"), bgIdx);
|
||||
|
||||
if (isIntenseBold && attr.IsIntense())
|
||||
if (attr.IsBold(isIntenseBold))
|
||||
{
|
||||
contentBuilder += "\\b";
|
||||
}
|
||||
|
||||
@@ -199,8 +199,6 @@ public:
|
||||
std::wstring GetCustomIdFromId(uint16_t id) const;
|
||||
void CopyHyperlinkMaps(const TextBuffer& OtherBuffer);
|
||||
|
||||
size_t SpanLength(const til::point coordStart, const til::point coordEnd) const;
|
||||
|
||||
std::wstring GetPlainText(til::point start, til::point end) const;
|
||||
|
||||
struct CopyRequest
|
||||
|
||||
@@ -287,7 +287,7 @@ namespace TerminalAppLocalTests
|
||||
NewTabArgs args{ newTerminalArgs };
|
||||
ActionAndArgs newTabAction{ ShortcutAction::NewTab, args };
|
||||
// push the arg onto the front
|
||||
page->_startupActions.Append(newTabAction);
|
||||
page->_startupActions.push_back(std::move(newTabAction));
|
||||
Log::Comment(L"Added a single newTab action");
|
||||
|
||||
auto app = ::winrt::Windows::UI::Xaml::Application::Current();
|
||||
|
||||
@@ -752,13 +752,11 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (const auto& realArgs = actionArgs.ActionArgs().try_as<ExecuteCommandlineArgs>())
|
||||
{
|
||||
auto actions = winrt::single_threaded_vector<ActionAndArgs>(
|
||||
TerminalPage::ConvertExecuteCommandlineToActions(realArgs));
|
||||
|
||||
if (actions.Size() != 0)
|
||||
auto actions = ConvertExecuteCommandlineToActions(realArgs);
|
||||
if (!actions.empty())
|
||||
{
|
||||
actionArgs.Handled(true);
|
||||
ProcessStartupActions(actions, false);
|
||||
ProcessStartupActions(std::move(actions), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,8 +81,6 @@ static winrt::hstring _GetErrorText(SettingsLoadErrors error)
|
||||
return _GetMessageText(static_cast<uint32_t>(error), settingsLoadErrorsLabels);
|
||||
}
|
||||
|
||||
static constexpr std::wstring_view StartupTaskName = L"StartTerminalOnLoginTask";
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// Function Description:
|
||||
@@ -184,8 +182,6 @@ namespace winrt::TerminalApp::implementation
|
||||
// this as a MTA, before the app is Create()'d
|
||||
WINRT_ASSERT(_loadedInitialSettings);
|
||||
|
||||
_ApplyLanguageSettingChange();
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalAppProvider,
|
||||
"AppCreated",
|
||||
@@ -335,8 +331,16 @@ namespace winrt::TerminalApp::implementation
|
||||
void AppLogic::_ApplyLanguageSettingChange() noexcept
|
||||
try
|
||||
{
|
||||
const auto language = _settings.GlobalSettings().Language();
|
||||
|
||||
if (!IsPackaged())
|
||||
{
|
||||
if (!language.empty())
|
||||
{
|
||||
// We cannot use the packaged app API, PrimaryLanguageOverride, but we *can* tell the resource loader
|
||||
// to set the Language for all loaded resources to the user's preferred language.
|
||||
winrt::Windows::ApplicationModel::Resources::Core::ResourceContext::SetGlobalQualifierValue(L"Language", language);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -344,8 +348,6 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
// NOTE: PrimaryLanguageOverride throws if this instance is unpackaged.
|
||||
const auto primaryLanguageOverride = ApplicationLanguages::PrimaryLanguageOverride();
|
||||
const auto language = _settings.GlobalSettings().Language();
|
||||
|
||||
if (primaryLanguageOverride != language)
|
||||
{
|
||||
ApplicationLanguages::PrimaryLanguageOverride(language);
|
||||
@@ -353,40 +355,6 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
CATCH_LOG()
|
||||
|
||||
safe_void_coroutine AppLogic::_ApplyStartupTaskStateChange()
|
||||
try
|
||||
{
|
||||
// First, make sure we're running in a packaged context. This method
|
||||
// won't work, and will crash mysteriously if we're running unpackaged.
|
||||
if (!IsPackaged())
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
|
||||
const auto tryEnableStartupTask = _settings.GlobalSettings().StartOnUserLogin();
|
||||
const auto task = co_await StartupTask::GetAsync(StartupTaskName);
|
||||
|
||||
switch (task.State())
|
||||
{
|
||||
case StartupTaskState::Disabled:
|
||||
if (tryEnableStartupTask)
|
||||
{
|
||||
co_await task.RequestEnableAsync();
|
||||
}
|
||||
break;
|
||||
case StartupTaskState::DisabledByUser:
|
||||
// TODO: GH#6254: define UX for other StartupTaskStates
|
||||
break;
|
||||
case StartupTaskState::Enabled:
|
||||
if (!tryEnableStartupTask)
|
||||
{
|
||||
task.Disable();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
// Method Description:
|
||||
// - Reloads the settings from the settings.json file.
|
||||
// - When this is called the first time, this initializes our settings. See
|
||||
@@ -424,7 +392,7 @@ namespace winrt::TerminalApp::implementation
|
||||
auto ev = winrt::make_self<SettingsLoadEventArgs>(true,
|
||||
static_cast<uint64_t>(_settingsLoadedResult),
|
||||
_settingsLoadExceptionText,
|
||||
warnings,
|
||||
warnings.GetView(),
|
||||
_settings);
|
||||
SettingsChanged.raise(*this, *ev);
|
||||
return;
|
||||
@@ -435,6 +403,9 @@ namespace winrt::TerminalApp::implementation
|
||||
_settings.LogSettingChanges(true);
|
||||
}
|
||||
|
||||
_ApplyLanguageSettingChange();
|
||||
_ProcessLazySettingsChanges();
|
||||
|
||||
if (initialLoad)
|
||||
{
|
||||
// Register for directory change notification.
|
||||
@@ -445,10 +416,6 @@ namespace winrt::TerminalApp::implementation
|
||||
// Here, we successfully reloaded the settings, and created a new
|
||||
// TerminalSettings object.
|
||||
|
||||
_ApplyLanguageSettingChange();
|
||||
_ApplyStartupTaskStateChange();
|
||||
_ProcessLazySettingsChanges();
|
||||
|
||||
auto warnings{ winrt::multi_threaded_vector<SettingsLoadWarnings>() };
|
||||
for (auto&& warn : _warnings)
|
||||
{
|
||||
@@ -457,7 +424,7 @@ namespace winrt::TerminalApp::implementation
|
||||
auto ev = winrt::make_self<SettingsLoadEventArgs>(!initialLoad,
|
||||
_settingsLoadedResult,
|
||||
_settingsLoadExceptionText,
|
||||
warnings,
|
||||
warnings.GetView(),
|
||||
_settings);
|
||||
SettingsChanged.raise(*this, *ev);
|
||||
}
|
||||
@@ -473,7 +440,6 @@ namespace winrt::TerminalApp::implementation
|
||||
// Both LoadSettings and ReloadSettings are supposed to call this function,
|
||||
// but LoadSettings skips it, so that the UI starts up faster.
|
||||
// Now that the UI is present we can do them with a less significant UX impact.
|
||||
_ApplyStartupTaskStateChange();
|
||||
_ProcessLazySettingsChanges();
|
||||
|
||||
FILETIME creationTime, exitTime, kernelTime, userTime, now;
|
||||
@@ -525,7 +491,7 @@ namespace winrt::TerminalApp::implementation
|
||||
auto ev = winrt::make_self<SettingsLoadEventArgs>(false,
|
||||
_settingsLoadedResult,
|
||||
_settingsLoadExceptionText,
|
||||
warnings,
|
||||
warnings.GetView(),
|
||||
_settings);
|
||||
|
||||
auto window = winrt::make_self<implementation::TerminalWindow>(*ev, _contentManager);
|
||||
|
||||
@@ -76,7 +76,6 @@ namespace winrt::TerminalApp::implementation
|
||||
TerminalApp::ContentManager _contentManager{ winrt::make<implementation::ContentManager>() };
|
||||
|
||||
void _ApplyLanguageSettingChange() noexcept;
|
||||
safe_void_coroutine _ApplyStartupTaskStateChange();
|
||||
|
||||
[[nodiscard]] HRESULT _TryLoadSettings() noexcept;
|
||||
void _ProcessLazySettingsChanges();
|
||||
|
||||
@@ -359,7 +359,7 @@ namespace winrt::TerminalApp::implementation
|
||||
_switchToMode(CommandPaletteMode::CommandlineMode);
|
||||
e.Handled(true);
|
||||
}
|
||||
else if (key == VirtualKey::C && ctrlDown)
|
||||
else if ((key == VirtualKey::C || key == VirtualKey::Insert) && ctrlDown)
|
||||
{
|
||||
_searchBox().CopySelectionToClipboard();
|
||||
e.Handled(true);
|
||||
|
||||
@@ -60,9 +60,12 @@ namespace winrt::Microsoft::TerminalApp::implementation
|
||||
|
||||
DebugTapConnection::DebugTapConnection(ITerminalConnection wrappedConnection)
|
||||
{
|
||||
_outputRevoker = wrappedConnection.TerminalOutput(winrt::auto_revoke, { this, &DebugTapConnection::_OutputHandler });
|
||||
_stateChangedRevoker = wrappedConnection.StateChanged(winrt::auto_revoke, [this](auto&& /*s*/, auto&& /*e*/) {
|
||||
StateChanged.raise(*this, nullptr);
|
||||
_outputRevoker = wrappedConnection.TerminalOutput(winrt::auto_revoke, { get_weak(), &DebugTapConnection::_OutputHandler });
|
||||
_stateChangedRevoker = wrappedConnection.StateChanged(winrt::auto_revoke, [weak = get_weak()](auto&& /*s*/, auto&& /*e*/) {
|
||||
if (const auto self = weak.get())
|
||||
{
|
||||
self->StateChanged.raise(*self, nullptr);
|
||||
}
|
||||
});
|
||||
_wrappedConnection = wrappedConnection;
|
||||
}
|
||||
|
||||
@@ -697,12 +697,24 @@ bool Pane::SwapPanes(std::shared_ptr<Pane> first, std::shared_ptr<Pane> second)
|
||||
// Refocus the last pane if there was a pane focused
|
||||
if (const auto focus = first->GetActivePane())
|
||||
{
|
||||
focus->_Focus();
|
||||
// GH#18184: manually focus the pane and content.
|
||||
// _Focus() results in no-op because the pane was _lastActive
|
||||
focus->GotFocus.raise(focus, FocusState::Programmatic);
|
||||
if (const auto& lastContent{ focus->GetLastFocusedContent() })
|
||||
{
|
||||
lastContent.Focus(FocusState::Programmatic);
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto focus = second->GetActivePane())
|
||||
{
|
||||
focus->_Focus();
|
||||
// GH#18184: manually focus the pane and content.
|
||||
// _Focus() results in no-op because the pane was _lastActive
|
||||
focus->GotFocus.raise(focus, FocusState::Programmatic);
|
||||
if (const auto& lastContent{ focus->GetLastFocusedContent() })
|
||||
{
|
||||
lastContent.Focus(FocusState::Programmatic);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
CommandlineArgs::CommandlineArgs(winrt::array_view<const winrt::hstring> args, winrt::hstring currentDirectory, uint32_t showWindowCommand, winrt::hstring envString) :
|
||||
_args{ args.begin(), args.end() },
|
||||
_cwd{ std::move(currentDirectory) },
|
||||
CurrentDirectory{ std::move(currentDirectory) },
|
||||
ShowWindowCommand{ showWindowCommand },
|
||||
CurrentEnvironment{ std::move(envString) }
|
||||
{
|
||||
|
||||
@@ -36,7 +36,6 @@ namespace winrt::TerminalApp::implementation
|
||||
::TerminalApp::AppCommandlineArgs _parsed;
|
||||
int32_t _parseResult = 0;
|
||||
winrt::com_array<winrt::hstring> _args;
|
||||
winrt::hstring _cwd;
|
||||
};
|
||||
|
||||
struct RequestReceiveContentArgs : RequestReceiveContentArgsT<RequestReceiveContentArgs>
|
||||
|
||||
@@ -206,7 +206,7 @@
|
||||
<value>ウィンドウを閉じる</value>
|
||||
</data>
|
||||
<data name="SplitTabText" xml:space="preserve">
|
||||
<value>[分割] タブ</value>
|
||||
<value>タブを分割</value>
|
||||
</data>
|
||||
<data name="SplitPaneText" xml:space="preserve">
|
||||
<value>ウィンドウを分割する</value>
|
||||
@@ -224,7 +224,7 @@
|
||||
<value>リセット</value>
|
||||
</data>
|
||||
<data name="RenameTabText" xml:space="preserve">
|
||||
<value>[名前の変更] タブ</value>
|
||||
<value>タブ名を変更</value>
|
||||
</data>
|
||||
<data name="DuplicateTabText" xml:space="preserve">
|
||||
<value>タブを複製する</value>
|
||||
|
||||
@@ -12,14 +12,14 @@ namespace winrt::TerminalApp::implementation
|
||||
WINRT_PROPERTY(bool, Reload, false);
|
||||
WINRT_PROPERTY(uint64_t, Result, S_OK);
|
||||
WINRT_PROPERTY(winrt::hstring, ExceptionText, L"");
|
||||
WINRT_PROPERTY(winrt::Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::SettingsLoadWarnings>, Warnings, nullptr);
|
||||
WINRT_PROPERTY(winrt::Windows::Foundation::Collections::IVectorView<Microsoft::Terminal::Settings::Model::SettingsLoadWarnings>, Warnings, nullptr);
|
||||
WINRT_PROPERTY(Microsoft::Terminal::Settings::Model::CascadiaSettings, NewSettings, nullptr);
|
||||
|
||||
public:
|
||||
SettingsLoadEventArgs(bool reload,
|
||||
uint64_t result,
|
||||
winrt::hstring exceptionText,
|
||||
winrt::Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::SettingsLoadWarnings> warnings,
|
||||
winrt::Windows::Foundation::Collections::IVectorView<Microsoft::Terminal::Settings::Model::SettingsLoadWarnings> warnings,
|
||||
Microsoft::Terminal::Settings::Model::CascadiaSettings newSettings) :
|
||||
_Reload{ reload },
|
||||
_Result{ result },
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "pch.h"
|
||||
#include "ShortcutActionDispatch.h"
|
||||
#include "WtExeUtils.h"
|
||||
|
||||
#include "ShortcutActionDispatch.g.cpp"
|
||||
|
||||
@@ -53,11 +54,22 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
if (handled)
|
||||
{
|
||||
#if defined(WT_BRANDING_RELEASE)
|
||||
constexpr uint8_t branding = 3;
|
||||
#elif defined(WT_BRANDING_PREVIEW)
|
||||
constexpr uint8_t branding = 2;
|
||||
#elif defined(WT_BRANDING_CANARY)
|
||||
constexpr uint8_t branding = 1;
|
||||
#else
|
||||
constexpr uint8_t branding = 0;
|
||||
#endif
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalAppProvider,
|
||||
"ActionDispatched",
|
||||
TraceLoggingDescription("Event emitted when an action was successfully performed"),
|
||||
TraceLoggingValue(static_cast<int>(actionAndArgs.Action()), "Action"),
|
||||
TraceLoggingValue(branding, "Branding"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
|
||||
}
|
||||
|
||||
@@ -230,6 +230,7 @@
|
||||
VerticalScrollMode="Enabled"
|
||||
Visibility="Visible">
|
||||
<TextBlock x:Name="_descriptionComment"
|
||||
Margin="0,0,20,0"
|
||||
IsTextSelectionEnabled="True"
|
||||
TextWrapping="WrapWholeWords" />
|
||||
</ScrollViewer>
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
// NOTE: `TerminalPage::_HandleCloseTabRequested` relies on the content being null after this call.
|
||||
Content(nullptr);
|
||||
}
|
||||
|
||||
|
||||
@@ -158,6 +158,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// Set this tab's icon to the icon from the content
|
||||
_UpdateTabIcon(*newTabImpl);
|
||||
|
||||
// This is necessary, because WinUI does not have support for middle clicks.
|
||||
// Its Tapped event doesn't provide the information what button was used either.
|
||||
tabViewItem.PointerPressed({ this, &TerminalPage::_OnTabPointerPressed });
|
||||
tabViewItem.PointerReleased({ this, &TerminalPage::_OnTabPointerReleased });
|
||||
tabViewItem.PointerExited({ this, &TerminalPage::_OnTabPointerExited });
|
||||
@@ -903,19 +905,39 @@ namespace winrt::TerminalApp::implementation
|
||||
if (_tabPointerMiddleButtonPressed && !eventArgs.GetCurrentPoint(nullptr).Properties().IsMiddleButtonPressed())
|
||||
{
|
||||
_tabPointerMiddleButtonPressed = false;
|
||||
if (const auto tabViewItem{ sender.try_as<MUX::Controls::TabViewItem>() })
|
||||
if (auto tabViewItem{ sender.try_as<MUX::Controls::TabViewItem>() })
|
||||
{
|
||||
tabViewItem.ReleasePointerCapture(eventArgs.Pointer());
|
||||
auto tab = _GetTabByTabViewItem(tabViewItem);
|
||||
if (!_tabPointerMiddleButtonExited && tab)
|
||||
if (!_tabPointerMiddleButtonExited)
|
||||
{
|
||||
_HandleCloseTabRequested(tab);
|
||||
_OnTabPointerReleasedCloseTab(std::move(tabViewItem));
|
||||
}
|
||||
}
|
||||
eventArgs.Handled(true);
|
||||
}
|
||||
}
|
||||
|
||||
safe_void_coroutine TerminalPage::_OnTabPointerReleasedCloseTab(winrt::Microsoft::UI::Xaml::Controls::TabViewItem sender)
|
||||
{
|
||||
const auto tab = _GetTabByTabViewItem(sender);
|
||||
if (!tab)
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
|
||||
// WinUI asynchronously updates its tab view items, so it may happen that we're given a
|
||||
// `TabViewItem` that still contains a `TabBase` which has actually already been removed.
|
||||
// First we must yield once, to flush out whatever TabView is currently doing.
|
||||
const auto strong = get_strong();
|
||||
co_await wil::resume_foreground(Dispatcher());
|
||||
|
||||
// `tab.Shutdown()` in `_RemoveTab()` sets the content to null = This checks if the tab is closed.
|
||||
if (tab.Content())
|
||||
{
|
||||
_HandleCloseTabRequested(tab);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Tracking pointer state for tab remove
|
||||
// Arguments:
|
||||
|
||||
@@ -51,8 +51,10 @@ namespace winrt::TerminalApp::implementation
|
||||
_tabStatusChangedRevoker = status.PropertyChanged(winrt::auto_revoke, [weakThis{ get_weak() }](auto& /*sender*/, auto& /*e*/) {
|
||||
// Sometimes nested bindings do not get updated,
|
||||
// thus let's notify property changed on TabStatus when one of its properties changes
|
||||
auto item{ weakThis.get() };
|
||||
item->PropertyChanged.raise(*item, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"TabStatus" });
|
||||
if (auto item{ weakThis.get() })
|
||||
{
|
||||
item->PropertyChanged.raise(*item, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"TabStatus" });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,6 @@ namespace winrt::TerminalApp::implementation
|
||||
TerminalPage::TerminalPage(TerminalApp::WindowProperties properties, const TerminalApp::ContentManager& manager) :
|
||||
_tabs{ winrt::single_threaded_observable_vector<TerminalApp::TabBase>() },
|
||||
_mruTabs{ winrt::single_threaded_observable_vector<TerminalApp::TabBase>() },
|
||||
_startupActions{ winrt::single_threaded_vector<ActionAndArgs>() },
|
||||
_manager{ manager },
|
||||
_hostingHwnd{},
|
||||
_WindowProperties{ std::move(properties) }
|
||||
@@ -297,7 +296,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// GH#12267: Don't forget about defterm handoff here. If we're being
|
||||
// created for embedding, then _yea_, we don't need to handoff to an
|
||||
// elevated window.
|
||||
if (!_startupActions || IsRunningElevated() || _shouldStartInboundListener || _startupActions.Size() == 0)
|
||||
if (_startupActions.empty() || IsRunningElevated() || _shouldStartInboundListener)
|
||||
{
|
||||
// there aren't startup actions, or we're elevated. In that case, go for it.
|
||||
return false;
|
||||
@@ -375,7 +374,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
void TerminalPage::HandoffToElevated(const CascadiaSettings& settings)
|
||||
{
|
||||
if (!_startupActions)
|
||||
if (_startupActions.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -489,7 +488,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
_startupState = StartupState::InStartup;
|
||||
|
||||
ProcessStartupActions(_startupActions, true);
|
||||
ProcessStartupActions(std::move(_startupActions), true);
|
||||
|
||||
// If we were told that the COM server needs to be started to listen for incoming
|
||||
// default application connections, start it now.
|
||||
@@ -546,80 +545,59 @@ namespace winrt::TerminalApp::implementation
|
||||
// nt -d .` from inside another directory to work as expected.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
safe_void_coroutine TerminalPage::ProcessStartupActions(Windows::Foundation::Collections::IVector<ActionAndArgs> actions,
|
||||
const bool initial,
|
||||
const winrt::hstring cwd,
|
||||
const winrt::hstring env)
|
||||
safe_void_coroutine TerminalPage::ProcessStartupActions(std::vector<ActionAndArgs> actions, const bool initial, const winrt::hstring cwd, const winrt::hstring env)
|
||||
{
|
||||
auto weakThis{ get_weak() };
|
||||
|
||||
// Handle it on a subsequent pass of the UI thread.
|
||||
co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::Normal);
|
||||
const auto strong = get_strong();
|
||||
|
||||
// If the caller provided a CWD, "switch" to that directory, then switch
|
||||
// back once we're done. This looks weird though, because we have to set
|
||||
// up the scope_exit _first_. We'll release the scope_exit if we don't
|
||||
// actually need it.
|
||||
|
||||
// back once we're done.
|
||||
auto originalVirtualCwd{ _WindowProperties.VirtualWorkingDirectory() };
|
||||
auto restoreCwd = wil::scope_exit([&originalVirtualCwd, this]() {
|
||||
// ignore errors, we'll just power on through. We'd rather do
|
||||
// something rather than fail silently if the directory doesn't
|
||||
// actually exist.
|
||||
_WindowProperties.VirtualWorkingDirectory(originalVirtualCwd);
|
||||
});
|
||||
|
||||
// Literally the same thing with env vars too
|
||||
auto originalVirtualEnv{ _WindowProperties.VirtualEnvVars() };
|
||||
auto restoreEnv = wil::scope_exit([&originalVirtualEnv, this]() {
|
||||
_WindowProperties.VirtualEnvVars(originalVirtualEnv);
|
||||
auto restoreCwd = wil::scope_exit([&]() {
|
||||
if (!cwd.empty())
|
||||
{
|
||||
// ignore errors, we'll just power on through. We'd rather do
|
||||
// something rather than fail silently if the directory doesn't
|
||||
// actually exist.
|
||||
_WindowProperties.VirtualWorkingDirectory(originalVirtualCwd);
|
||||
_WindowProperties.VirtualEnvVars(originalVirtualEnv);
|
||||
}
|
||||
});
|
||||
|
||||
if (cwd.empty())
|
||||
{
|
||||
// We didn't actually need to change the virtual CWD, so we don't
|
||||
// need to restore it
|
||||
restoreCwd.release();
|
||||
}
|
||||
else
|
||||
if (!cwd.empty())
|
||||
{
|
||||
_WindowProperties.VirtualWorkingDirectory(cwd);
|
||||
}
|
||||
|
||||
if (env.empty())
|
||||
{
|
||||
restoreEnv.release();
|
||||
}
|
||||
else
|
||||
{
|
||||
_WindowProperties.VirtualEnvVars(env);
|
||||
}
|
||||
|
||||
if (auto page{ weakThis.get() })
|
||||
for (size_t i = 0; i < actions.size(); ++i)
|
||||
{
|
||||
for (const auto& action : actions)
|
||||
if (i != 0)
|
||||
{
|
||||
if (auto page{ weakThis.get() })
|
||||
{
|
||||
_actionDispatch->DoAction(action);
|
||||
}
|
||||
else
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
// Each action may rely on the XAML layout of a preceding action.
|
||||
// Most importantly, this is the case for the combination of NewTab + SplitPane,
|
||||
// as the former appears to only have a layout size after at least 1 resume_foreground,
|
||||
// while the latter relies on that information. This is also why it uses Low priority.
|
||||
//
|
||||
// Curiously, this does not seem to be required when using startupActions, but only when
|
||||
// tearing out a tab (this currently creates a new window with injected startup actions).
|
||||
// This indicates that this is really more of an architectural issue and not a fundamental one.
|
||||
co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::Low);
|
||||
}
|
||||
|
||||
// GH#6586: now that we're done processing all startup commands,
|
||||
// focus the active control. This will work as expected for both
|
||||
// commandline invocations and for `wt` action invocations.
|
||||
if (const auto& terminalTab{ _GetFocusedTabImpl() })
|
||||
_actionDispatch->DoAction(actions[i]);
|
||||
}
|
||||
|
||||
// GH#6586: now that we're done processing all startup commands,
|
||||
// focus the active control. This will work as expected for both
|
||||
// commandline invocations and for `wt` action invocations.
|
||||
if (const auto& terminalTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
if (const auto& content{ terminalTab->GetActiveContent() })
|
||||
{
|
||||
if (const auto& content{ terminalTab->GetActiveContent() })
|
||||
{
|
||||
content.Focus(FocusState::Programmatic);
|
||||
}
|
||||
content.Focus(FocusState::Programmatic);
|
||||
}
|
||||
}
|
||||
|
||||
if (initial)
|
||||
{
|
||||
_CompleteInitialization();
|
||||
@@ -1783,16 +1761,22 @@ namespace winrt::TerminalApp::implementation
|
||||
auto tab{ weakTab.get() };
|
||||
if (page && tab)
|
||||
{
|
||||
if (args.PropertyName() == L"Title")
|
||||
const auto propertyName = args.PropertyName();
|
||||
if (propertyName == L"Title")
|
||||
{
|
||||
page->_UpdateTitle(*tab);
|
||||
}
|
||||
else if (args.PropertyName() == L"Content")
|
||||
else if (propertyName == L"Content")
|
||||
{
|
||||
if (*tab == page->_GetFocusedTab())
|
||||
{
|
||||
page->_tabContent.Children().Clear();
|
||||
page->_tabContent.Children().Append(tab->Content());
|
||||
const auto children = page->_tabContent.Children();
|
||||
|
||||
children.Clear();
|
||||
if (auto content = tab->Content())
|
||||
{
|
||||
page->_tabContent.Children().Append(std::move(content));
|
||||
}
|
||||
|
||||
tab->Focus(FocusState::Programmatic);
|
||||
}
|
||||
@@ -1988,6 +1972,12 @@ namespace winrt::TerminalApp::implementation
|
||||
actions.insert(actions.end(), std::make_move_iterator(tabActions.begin()), std::make_move_iterator(tabActions.end()));
|
||||
}
|
||||
|
||||
// Avoid persisting a window with zero tabs, because `BuildStartupActions` happened to return an empty vector.
|
||||
if (actions.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// if the focused tab was not the last tab, restore that
|
||||
auto idx = _GetFocusedTabIndex();
|
||||
if (idx && idx != tabCount - 1)
|
||||
@@ -2313,7 +2303,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// for it. The Title change will be propagated upwards through the tab's
|
||||
// PropertyChanged event handler.
|
||||
void TerminalPage::_activePaneChanged(winrt::TerminalApp::TerminalTab sender,
|
||||
Windows::Foundation::IInspectable args)
|
||||
Windows::Foundation::IInspectable /*args*/)
|
||||
{
|
||||
if (const auto tab{ _GetTerminalTabImpl(sender) })
|
||||
{
|
||||
@@ -3658,13 +3648,9 @@ namespace winrt::TerminalApp::implementation
|
||||
// - actions: a list of Actions to process on startup.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalPage::SetStartupActions(std::vector<ActionAndArgs>& actions)
|
||||
void TerminalPage::SetStartupActions(std::vector<ActionAndArgs> actions)
|
||||
{
|
||||
// The fastest way to copy all the actions out of the std::vector and
|
||||
// put them into a winrt::IVector is by making a copy, then moving the
|
||||
// copy into the winrt vector ctor.
|
||||
auto listCopy = actions;
|
||||
_startupActions = winrt::single_threaded_vector<ActionAndArgs>(std::move(listCopy));
|
||||
_startupActions = std::move(actions);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -4179,6 +4165,13 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
});
|
||||
|
||||
sui.ShowLoadWarningsDialog([weakThis{ get_weak() }](auto&& /*s*/, const Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Settings::Model::SettingsLoadWarnings>& warnings) {
|
||||
if (auto page{ weakThis.get() })
|
||||
{
|
||||
page->ShowLoadWarningsDialog.raise(*page, warnings);
|
||||
}
|
||||
});
|
||||
|
||||
return *settingsContent;
|
||||
}
|
||||
|
||||
@@ -5269,8 +5262,8 @@ namespace winrt::TerminalApp::implementation
|
||||
_sendDraggedTabToWindow(winrt::to_hstring(args.TargetWindow()), args.TabIndex(), std::nullopt);
|
||||
}
|
||||
|
||||
void TerminalPage::_onTabDroppedOutside(winrt::IInspectable sender,
|
||||
winrt::MUX::Controls::TabViewTabDroppedOutsideEventArgs e)
|
||||
void TerminalPage::_onTabDroppedOutside(winrt::IInspectable /*sender*/,
|
||||
winrt::MUX::Controls::TabViewTabDroppedOutsideEventArgs /*e*/)
|
||||
{
|
||||
// Get the current pointer point from the CoreWindow
|
||||
const auto& pointerPoint{ CoreWindow::GetForCurrentThread().PointerPosition() };
|
||||
|
||||
@@ -128,7 +128,7 @@ namespace winrt::TerminalApp::implementation
|
||||
void Maximized(bool newMaximized);
|
||||
void RequestSetMaximized(bool newMaximized);
|
||||
|
||||
void SetStartupActions(std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs>& actions);
|
||||
void SetStartupActions(std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> actions);
|
||||
|
||||
void SetInboundListener(bool isEmbedding);
|
||||
static std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> ConvertExecuteCommandlineToActions(const Microsoft::Terminal::Settings::Model::ExecuteCommandlineArgs& args);
|
||||
@@ -146,7 +146,7 @@ namespace winrt::TerminalApp::implementation
|
||||
void ActionSaveFailed(winrt::hstring message);
|
||||
void ShowTerminalWorkingDirectory();
|
||||
|
||||
safe_void_coroutine ProcessStartupActions(Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::ActionAndArgs> actions,
|
||||
safe_void_coroutine ProcessStartupActions(std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> actions,
|
||||
const bool initial,
|
||||
const winrt::hstring cwd = winrt::hstring{},
|
||||
const winrt::hstring env = winrt::hstring{});
|
||||
@@ -187,6 +187,7 @@ namespace winrt::TerminalApp::implementation
|
||||
til::typed_event<IInspectable, IInspectable> OpenSystemMenu;
|
||||
til::typed_event<IInspectable, IInspectable> QuitRequested;
|
||||
til::typed_event<IInspectable, winrt::Microsoft::Terminal::Control::ShowWindowArgs> ShowWindowChanged;
|
||||
til::typed_event<Windows::Foundation::IInspectable, Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Settings::Model::SettingsLoadWarnings>> ShowLoadWarningsDialog;
|
||||
|
||||
til::typed_event<Windows::Foundation::IInspectable, winrt::TerminalApp::RequestMoveContentArgs> RequestMoveContent;
|
||||
til::typed_event<Windows::Foundation::IInspectable, winrt::TerminalApp::RequestReceiveContentArgs> RequestReceiveContent;
|
||||
@@ -255,7 +256,7 @@ namespace winrt::TerminalApp::implementation
|
||||
winrt::Windows::UI::Xaml::Controls::Grid::LayoutUpdated_revoker _layoutUpdatedRevoker;
|
||||
StartupState _startupState{ StartupState::NotInitialized };
|
||||
|
||||
Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::ActionAndArgs> _startupActions;
|
||||
std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> _startupActions;
|
||||
bool _shouldStartInboundListener{ false };
|
||||
bool _isEmbeddingInboundListener{ false };
|
||||
|
||||
@@ -433,6 +434,7 @@ namespace winrt::TerminalApp::implementation
|
||||
bool _tabPointerMiddleButtonExited{ false };
|
||||
void _OnTabPointerPressed(const IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& eventArgs);
|
||||
void _OnTabPointerReleased(const IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& eventArgs);
|
||||
safe_void_coroutine _OnTabPointerReleasedCloseTab(winrt::Microsoft::UI::Xaml::Controls::TabViewItem sender);
|
||||
void _OnTabPointerEntered(const IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& eventArgs);
|
||||
void _OnTabPointerExited(const IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& eventArgs);
|
||||
|
||||
|
||||
@@ -97,6 +97,7 @@ namespace TerminalApp
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> OpenSystemMenu;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Control.ShowWindowArgs> ShowWindowChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Windows.Foundation.Collections.IVectorView<Microsoft.Terminal.Settings.Model.SettingsLoadWarnings> > ShowLoadWarningsDialog;
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, RequestMoveContentArgs> RequestMoveContent;
|
||||
event Windows.Foundation.TypedEventHandler<Object, RequestReceiveContentArgs> RequestReceiveContent;
|
||||
|
||||
@@ -851,6 +851,9 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
// Don't forget to call the overridden function. :)
|
||||
TabBase::Shutdown();
|
||||
|
||||
if (_rootPane)
|
||||
{
|
||||
_rootPane->Shutdown();
|
||||
|
||||
@@ -144,7 +144,6 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// Now that we know we can do XAML, build our page.
|
||||
_root = winrt::make_self<TerminalPage>(*_WindowProperties, _manager);
|
||||
_dialog = ContentDialog{};
|
||||
|
||||
// Pass in information about the initial state of the window.
|
||||
// * If we were supposed to start from serialized "content", do that,
|
||||
@@ -220,6 +219,7 @@ namespace winrt::TerminalApp::implementation
|
||||
_root->Initialized({ get_weak(), &TerminalWindow::_pageInitialized });
|
||||
_root->WindowSizeChanged({ get_weak(), &TerminalWindow::_WindowSizeChanged });
|
||||
_root->RenameWindowRequested({ get_weak(), &TerminalWindow::_RenameWindowRequested });
|
||||
_root->ShowLoadWarningsDialog({ get_weak(), &TerminalWindow::_ShowLoadWarningsDialog });
|
||||
_root->Create();
|
||||
|
||||
AppLogic::Current()->SettingsChanged({ get_weak(), &TerminalWindow::UpdateSettingsHandler });
|
||||
@@ -313,6 +313,15 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
return _settings.GlobalSettings().CurrentTheme();
|
||||
}
|
||||
|
||||
// WinUI can't show 2 dialogs simultaneously. Yes, really. If you do, you get an exception.
|
||||
// As such, we must dismiss whatever dialog is currently being shown.
|
||||
//
|
||||
// This limit is of course per-thread and not per-window. Yes... really. See:
|
||||
// https://github.com/microsoft/microsoft-ui-xaml/issues/794
|
||||
// The consequence is that we use a static variable to keep track of the shown dialog.
|
||||
static ContentDialog s_activeDialog{ nullptr };
|
||||
|
||||
// Method Description:
|
||||
// - Show a ContentDialog with buttons to take further action. Uses the
|
||||
// FrameworkElements provided as the title and content of this dialog, and
|
||||
@@ -328,16 +337,32 @@ namespace winrt::TerminalApp::implementation
|
||||
// - an IAsyncOperation with the dialog result
|
||||
winrt::Windows::Foundation::IAsyncOperation<ContentDialogResult> TerminalWindow::ShowDialog(winrt::WUX::Controls::ContentDialog dialog)
|
||||
{
|
||||
// DON'T release this lock in a wil::scope_exit. The scope_exit will get
|
||||
// called when we await, which is not what we want.
|
||||
std::unique_lock lock{ _dialogLock, std::try_to_lock };
|
||||
if (!lock)
|
||||
// As mentioned on s_activeDialog, dismissing the active dialog is necessary.
|
||||
// We repeat it a few times in case the resume_foreground failed to work,
|
||||
// but I found that one iteration will always be enough in practice.
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
if (!s_activeDialog)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
s_activeDialog.Hide();
|
||||
|
||||
// Wait for the current dialog to be hidden.
|
||||
co_await wil::resume_foreground(_root->Dispatcher(), CoreDispatcherPriority::Low);
|
||||
}
|
||||
|
||||
// If two sources call ShowDialog() simultaneously, it may happen that both enter the above loop,
|
||||
// but it's crucial that only one of them continues below as only 1 dialog can be shown at a time.
|
||||
// Thankfully, everything runs on the UI thread, so only 1 caller will exit the above loop at a time.
|
||||
// So, if s_activeDialog is still set at this point, we must have lost the race.
|
||||
if (s_activeDialog)
|
||||
{
|
||||
// Another dialog is visible.
|
||||
co_return ContentDialogResult::None;
|
||||
}
|
||||
|
||||
_dialog = dialog;
|
||||
s_activeDialog = dialog;
|
||||
|
||||
// IMPORTANT: This is necessary as documented in the ContentDialog MSDN docs.
|
||||
// Since we're hosting the dialog in a Xaml island, we need to connect it to the
|
||||
@@ -367,23 +392,26 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
} };
|
||||
|
||||
themingLambda(dialog, nullptr); // if it's already in the tree
|
||||
auto loadedRevoker{ dialog.Loaded(winrt::auto_revoke, themingLambda) }; // if it's not yet in the tree
|
||||
auto result = ContentDialogResult::None;
|
||||
|
||||
// Display the dialog.
|
||||
co_return co_await dialog.ShowAsync(Controls::ContentDialogPlacement::Popup);
|
||||
// Extra scope to drop the revoker before resetting the s_activeDialog to null.
|
||||
{
|
||||
themingLambda(dialog, nullptr); // if it's already in the tree
|
||||
auto loadedRevoker{ dialog.Loaded(winrt::auto_revoke, themingLambda) }; // if it's not yet in the tree
|
||||
result = co_await dialog.ShowAsync(Controls::ContentDialogPlacement::Popup);
|
||||
}
|
||||
|
||||
// After the dialog is dismissed, the dialog lock (held by `lock`) will
|
||||
// be released so another can be shown
|
||||
s_activeDialog = nullptr;
|
||||
co_return result;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Dismiss the (only) visible ContentDialog
|
||||
void TerminalWindow::DismissDialog()
|
||||
{
|
||||
if (auto localDialog = std::exchange(_dialog, nullptr))
|
||||
if (s_activeDialog)
|
||||
{
|
||||
localDialog.Hide();
|
||||
s_activeDialog.Hide();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -449,7 +477,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// validating the settings.
|
||||
// - Only one dialog can be visible at a time. If another dialog is visible
|
||||
// when this is called, nothing happens. See ShowDialog for details
|
||||
void TerminalWindow::_ShowLoadWarningsDialog(const Windows::Foundation::Collections::IVector<SettingsLoadWarnings>& warnings)
|
||||
void TerminalWindow::_ShowLoadWarningsDialog(const IInspectable&, const Windows::Foundation::Collections::IVectorView<SettingsLoadWarnings>& warnings)
|
||||
{
|
||||
auto title = RS_(L"SettingsValidateErrorTitle");
|
||||
auto buttonText = RS_(L"Ok");
|
||||
@@ -509,7 +537,7 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
else if (settingsLoadedResult == S_FALSE)
|
||||
{
|
||||
_ShowLoadWarningsDialog(_initialLoadResult.Warnings());
|
||||
_ShowLoadWarningsDialog(nullptr, _initialLoadResult.Warnings());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -795,7 +823,7 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
else if (args.Result() == S_FALSE)
|
||||
{
|
||||
_ShowLoadWarningsDialog(args.Warnings());
|
||||
_ShowLoadWarningsDialog(nullptr, args.Warnings());
|
||||
}
|
||||
else if (args.Result() == S_OK)
|
||||
{
|
||||
@@ -1054,12 +1082,8 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
_contentBounds = bounds;
|
||||
|
||||
const auto& args = _contentStringToActions(content, true);
|
||||
|
||||
for (const auto& action : args)
|
||||
{
|
||||
_initialContentArgs.push_back(action);
|
||||
}
|
||||
const auto args = _contentStringToActions(content, true);
|
||||
_initialContentArgs = wil::to_vector(args);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -1085,7 +1109,7 @@ namespace winrt::TerminalApp::implementation
|
||||
if (_appArgs->ExitCode() == 0)
|
||||
{
|
||||
auto& parsedArgs = _appArgs->ParsedArgs();
|
||||
auto actions = winrt::single_threaded_vector<ActionAndArgs>(std::move(parsedArgs.GetStartupActions()));
|
||||
auto& actions = parsedArgs.GetStartupActions();
|
||||
|
||||
_root->ProcessStartupActions(actions, false, _appArgs->CurrentDirectory(), _appArgs->CurrentEnvironment());
|
||||
|
||||
@@ -1200,7 +1224,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
try
|
||||
{
|
||||
const auto& args = ActionAndArgs::Deserialize(content);
|
||||
const auto args = ActionAndArgs::Deserialize(content);
|
||||
if (args == nullptr ||
|
||||
args.Size() == 0)
|
||||
{
|
||||
@@ -1244,9 +1268,9 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
const bool replaceFirstWithNewTab = tabIndex >= _root->NumberOfTabs();
|
||||
|
||||
const auto& args = _contentStringToActions(content, replaceFirstWithNewTab);
|
||||
auto args = _contentStringToActions(content, replaceFirstWithNewTab);
|
||||
|
||||
_root->AttachContent(args, tabIndex);
|
||||
_root->AttachContent(std::move(args), tabIndex);
|
||||
}
|
||||
}
|
||||
void TerminalWindow::SendContentToOther(winrt::TerminalApp::RequestReceiveContentArgs args)
|
||||
|
||||
@@ -167,8 +167,6 @@ namespace winrt::TerminalApp::implementation
|
||||
// ALSO: If you add any UIElements as roots here, make sure they're
|
||||
// updated in _ApplyTheme. The root currently is _root.
|
||||
winrt::com_ptr<TerminalPage> _root{ nullptr };
|
||||
winrt::Windows::UI::Xaml::Controls::ContentDialog _dialog{ nullptr };
|
||||
std::shared_mutex _dialogLock;
|
||||
|
||||
wil::com_ptr<CommandlineArgs> _appArgs{ nullptr };
|
||||
bool _hasCommandLineArguments{ false };
|
||||
@@ -191,7 +189,7 @@ namespace winrt::TerminalApp::implementation
|
||||
const winrt::hstring& contentKey,
|
||||
HRESULT settingsLoadedResult,
|
||||
const winrt::hstring& exceptionText);
|
||||
void _ShowLoadWarningsDialog(const Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::SettingsLoadWarnings>& warnings);
|
||||
void _ShowLoadWarningsDialog(const IInspectable& sender, const Windows::Foundation::Collections::IVectorView<Microsoft::Terminal::Settings::Model::SettingsLoadWarnings>& warnings);
|
||||
|
||||
bool _IsKeyboardServiceEnabled();
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace TerminalApp
|
||||
{
|
||||
Boolean Reload { get; };
|
||||
UInt64 Result { get; };
|
||||
IVector<Microsoft.Terminal.Settings.Model.SettingsLoadWarnings> Warnings { get; };
|
||||
IVectorView<Microsoft.Terminal.Settings.Model.SettingsLoadWarnings> Warnings { get; };
|
||||
String ExceptionText { get; };
|
||||
|
||||
Microsoft.Terminal.Settings.Model.CascadiaSettings NewSettings { get; };
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include <winrt/Windows.ApplicationModel.h>
|
||||
#include <winrt/Windows.ApplicationModel.DataTransfer.h>
|
||||
#include <winrt/Windows.ApplicationModel.Resources.Core.h>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
#include <winrt/Windows.Foundation.Metadata.h>
|
||||
|
||||
@@ -14,6 +14,13 @@ static DWORD g_cTerminalHandoffRegistration = 0;
|
||||
// Mutex so we only do start/stop/establish one at a time.
|
||||
static std::shared_mutex _mtx;
|
||||
|
||||
// This is the callback that will be called when a connection is received.
|
||||
// Call this once during startup and don't ever change it again (race condition).
|
||||
void CTerminalHandoff::s_setCallback(NewHandoffFunction callback) noexcept
|
||||
{
|
||||
_pfnHandoff = callback;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Starts listening for TerminalHandoff requests by registering
|
||||
// our class and interface with COM.
|
||||
@@ -21,24 +28,19 @@ static std::shared_mutex _mtx;
|
||||
// - pfnHandoff - Function to callback when a handoff is received
|
||||
// Return Value:
|
||||
// - S_OK, E_NOT_VALID_STATE (start called when already started) or relevant COM registration error.
|
||||
HRESULT CTerminalHandoff::s_StartListening(NewHandoffFunction pfnHandoff)
|
||||
HRESULT CTerminalHandoff::s_StartListening()
|
||||
try
|
||||
{
|
||||
std::unique_lock lock{ _mtx };
|
||||
|
||||
RETURN_HR_IF(E_NOT_VALID_STATE, _pfnHandoff != nullptr);
|
||||
|
||||
const auto classFactory = Make<SimpleClassFactory<CTerminalHandoff>>();
|
||||
|
||||
RETURN_IF_NULL_ALLOC(classFactory);
|
||||
RETURN_LAST_ERROR_IF_NULL(classFactory);
|
||||
|
||||
ComPtr<IUnknown> unk;
|
||||
RETURN_IF_FAILED(classFactory.As(&unk));
|
||||
|
||||
RETURN_IF_FAILED(CoRegisterClassObject(__uuidof(CTerminalHandoff), unk.Get(), CLSCTX_LOCAL_SERVER, REGCLS_SINGLEUSE, &g_cTerminalHandoffRegistration));
|
||||
|
||||
_pfnHandoff = pfnHandoff;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN()
|
||||
@@ -53,15 +55,6 @@ 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;
|
||||
|
||||
if (g_cTerminalHandoffRegistration)
|
||||
{
|
||||
@@ -92,22 +85,15 @@ HRESULT CTerminalHandoff::EstablishPtyHandoff(HANDLE* in, HANDLE* out, HANDLE si
|
||||
{
|
||||
try
|
||||
{
|
||||
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.
|
||||
LOG_IF_FAILED(s_StopListeningLocked());
|
||||
LOG_IF_FAILED(s_StopListening());
|
||||
|
||||
// Report an error if no one registered a handoff function before calling this.
|
||||
THROW_HR_IF_NULL(E_NOT_VALID_STATE, localPfnHandoff);
|
||||
THROW_HR_IF_NULL(E_NOT_VALID_STATE, _pfnHandoff);
|
||||
|
||||
// Call registered handler from when we started listening.
|
||||
THROW_IF_FAILED(localPfnHandoff(in, out, signal, reference, server, client, startupInfo));
|
||||
THROW_IF_FAILED(_pfnHandoff(in, out, signal, reference, server, client, startupInfo));
|
||||
|
||||
#pragma warning(suppress : 26477)
|
||||
TraceLoggingWrite(
|
||||
|
||||
@@ -38,11 +38,11 @@ struct __declspec(uuid(__CLSID_CTerminalHandoff))
|
||||
|
||||
#pragma endregion
|
||||
|
||||
static HRESULT s_StartListening(NewHandoffFunction pfnHandoff);
|
||||
static HRESULT s_StopListening();
|
||||
static void s_setCallback(NewHandoffFunction callback) noexcept;
|
||||
static HRESULT s_StartListening();
|
||||
|
||||
private:
|
||||
static HRESULT s_StopListeningLocked();
|
||||
static HRESULT s_StopListening();
|
||||
};
|
||||
|
||||
// Disable warnings from the CoCreatableClass macro as the value it provides for
|
||||
|
||||
@@ -313,6 +313,13 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||
}
|
||||
CATCH_LOG()
|
||||
|
||||
try
|
||||
{
|
||||
auto processImageName{ wil::QueryFullProcessImageNameW<std::wstring>(_piClient.hProcess) };
|
||||
_clientName = std::filesystem::path{ std::move(processImageName) }.filename().wstring();
|
||||
}
|
||||
CATCH_LOG()
|
||||
|
||||
_pipe = std::move(pipe.server);
|
||||
*in = pipe.client.release();
|
||||
*out = pipeClientClone.release();
|
||||
@@ -428,6 +435,20 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||
TerminalOutput.raise(L"\r\n");
|
||||
TerminalOutput.raise(badPathText);
|
||||
}
|
||||
// If the requested action requires elevation, display appropriate message
|
||||
else if (hr == HRESULT_FROM_WIN32(ERROR_ELEVATION_REQUIRED))
|
||||
{
|
||||
const auto elevationText = RS_(L"ElevationRequired");
|
||||
TerminalOutput.raise(L"\r\n");
|
||||
TerminalOutput.raise(elevationText);
|
||||
}
|
||||
// If the requested executable was not found, display appropriate message
|
||||
else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
|
||||
{
|
||||
const auto fileNotFoundText = RS_(L"FileNotFound");
|
||||
TerminalOutput.raise(L"\r\n");
|
||||
TerminalOutput.raise(fileNotFoundText);
|
||||
}
|
||||
|
||||
_transitionToState(ConnectionState::Failed);
|
||||
|
||||
@@ -780,12 +801,12 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||
|
||||
void ConptyConnection::StartInboundListener()
|
||||
{
|
||||
THROW_IF_FAILED(CTerminalHandoff::s_StartListening(&ConptyConnection::NewHandoff));
|
||||
}
|
||||
static const auto init = []() noexcept {
|
||||
CTerminalHandoff::s_setCallback(&ConptyConnection::NewHandoff);
|
||||
return true;
|
||||
}();
|
||||
|
||||
void ConptyConnection::StopInboundListener()
|
||||
{
|
||||
THROW_IF_FAILED(CTerminalHandoff::s_StopListening());
|
||||
CTerminalHandoff::s_StartListening();
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
|
||||
@@ -36,7 +36,6 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||
WORD ShowWindow() const noexcept;
|
||||
|
||||
static void StartInboundListener();
|
||||
static void StopInboundListener();
|
||||
|
||||
static winrt::event_token NewConnection(const NewConnectionHandler& handler);
|
||||
static void NewConnection(const winrt::event_token& token);
|
||||
|
||||
@@ -23,7 +23,6 @@ namespace Microsoft.Terminal.TerminalConnection
|
||||
|
||||
static event NewConnectionHandler NewConnection;
|
||||
static void StartInboundListener();
|
||||
static void StopInboundListener();
|
||||
|
||||
static Windows.Foundation.Collections.ValueSet CreateSettings(String cmdline,
|
||||
String startingDirectory,
|
||||
|
||||
@@ -209,7 +209,7 @@
|
||||
</data>
|
||||
<data name="CtrlDToClose" xml:space="preserve">
|
||||
<value>Sie können dieses Terminal jetzt mit STRG+D schließen oder zum Neustart die EINGABETASTE drücken.</value>
|
||||
<comment>"Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter).</comment>
|
||||
<comment>"Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter).</comment>
|
||||
</data>
|
||||
<data name="ProcessFailedToLaunch" xml:space="preserve">
|
||||
<value>[Fehler {0} beim Start von `{1}']</value>
|
||||
@@ -220,4 +220,10 @@
|
||||
<value>Auf das Startverzeichnis „{0}“ konnte nicht zugegriffen werden</value>
|
||||
<comment>The first argument {0} is a path to a directory on the filesystem, as provided by the user.</comment>
|
||||
</data>
|
||||
<data name="ElevationRequired" xml:space="preserve">
|
||||
<value>Für den angeforderten Vorgang sind erhöhte Rechte erforderlich.</value>
|
||||
</data>
|
||||
<data name="FileNotFound" xml:space="preserve">
|
||||
<value>Die angegebene Datei wurde nicht gefunden.</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -209,7 +209,7 @@
|
||||
</data>
|
||||
<data name="CtrlDToClose" xml:space="preserve">
|
||||
<value>You can now close this terminal with Ctrl+D, or press Enter to restart.</value>
|
||||
<comment>"Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter).</comment>
|
||||
<comment>"Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter).</comment>
|
||||
</data>
|
||||
<data name="ProcessFailedToLaunch" xml:space="preserve">
|
||||
<value>[error {0} when launching `{1}']</value>
|
||||
@@ -220,4 +220,10 @@
|
||||
<value>Could not access starting directory "{0}"</value>
|
||||
<comment>The first argument {0} is a path to a directory on the filesystem, as provided by the user.</comment>
|
||||
</data>
|
||||
</root>
|
||||
<data name="ElevationRequired" xml:space="preserve">
|
||||
<value>The requested operation requires elevation.</value>
|
||||
</data>
|
||||
<data name="FileNotFound" xml:space="preserve">
|
||||
<value>The system cannot find the file specified.</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -209,7 +209,7 @@
|
||||
</data>
|
||||
<data name="CtrlDToClose" xml:space="preserve">
|
||||
<value>Ahora puede cerrar este terminal con Ctrl+D o presionar Entrar para reiniciar.</value>
|
||||
<comment>"Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter).</comment>
|
||||
<comment>"Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter).</comment>
|
||||
</data>
|
||||
<data name="ProcessFailedToLaunch" xml:space="preserve">
|
||||
<value>[error {0} al iniciar `{1}']</value>
|
||||
@@ -220,4 +220,10 @@
|
||||
<value>No se pudo obtener acceso al directorio inicial «{0}»</value>
|
||||
<comment>The first argument {0} is a path to a directory on the filesystem, as provided by the user.</comment>
|
||||
</data>
|
||||
<data name="ElevationRequired" xml:space="preserve">
|
||||
<value>La operación solicitada requiere elevación.</value>
|
||||
</data>
|
||||
<data name="FileNotFound" xml:space="preserve">
|
||||
<value>El sistema no puede encontrar el archivo especificado.</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -209,7 +209,7 @@
|
||||
</data>
|
||||
<data name="CtrlDToClose" xml:space="preserve">
|
||||
<value>Vous pouvez maintenant fermer ce terminal avec Ctrl+D, ou appuyez sur Entrée pour redémarrer.</value>
|
||||
<comment>"Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter).</comment>
|
||||
<comment>"Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter).</comment>
|
||||
</data>
|
||||
<data name="ProcessFailedToLaunch" xml:space="preserve">
|
||||
<value>[erreur {0} lors du lancement de `{1}']</value>
|
||||
@@ -220,4 +220,10 @@
|
||||
<value>Le démarrage du répertoire n’est pas accessible «{0}»</value>
|
||||
<comment>The first argument {0} is a path to a directory on the filesystem, as provided by the user.</comment>
|
||||
</data>
|
||||
<data name="ElevationRequired" xml:space="preserve">
|
||||
<value>L’opération demandée nécessite une élévation.</value>
|
||||
</data>
|
||||
<data name="FileNotFound" xml:space="preserve">
|
||||
<value>Le fichier spécifié est introuvable.</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -209,7 +209,7 @@
|
||||
</data>
|
||||
<data name="CtrlDToClose" xml:space="preserve">
|
||||
<value>È ora possibile chiudere il terminale con CTRL+D oppure premere INVIO per riavviarlo.</value>
|
||||
<comment>"Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter).</comment>
|
||||
<comment>"Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter).</comment>
|
||||
</data>
|
||||
<data name="ProcessFailedToLaunch" xml:space="preserve">
|
||||
<value>[errore {0} durante l'avvio di `{1}']</value>
|
||||
@@ -220,4 +220,10 @@
|
||||
<value>Non è possibile accedere alla directory di avvio "{0}"</value>
|
||||
<comment>The first argument {0} is a path to a directory on the filesystem, as provided by the user.</comment>
|
||||
</data>
|
||||
<data name="ElevationRequired" xml:space="preserve">
|
||||
<value>Per l'operazione richiesta è necessaria l'elevazione dei privilegi.</value>
|
||||
</data>
|
||||
<data name="FileNotFound" xml:space="preserve">
|
||||
<value>Impossibile trovare il file specificato.</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -209,7 +209,7 @@
|
||||
</data>
|
||||
<data name="CtrlDToClose" xml:space="preserve">
|
||||
<value>このターミナルを Ctrl+D で閉じるか、Enter キーを押して再起動できます。</value>
|
||||
<comment>"Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter).</comment>
|
||||
<comment>"Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter).</comment>
|
||||
</data>
|
||||
<data name="ProcessFailedToLaunch" xml:space="preserve">
|
||||
<value>['{1}' の起動時にエラー {0} が発生しました]</value>
|
||||
@@ -220,4 +220,10 @@
|
||||
<value>先頭のディレクトリ "{0}" にアクセスできませんでした</value>
|
||||
<comment>The first argument {0} is a path to a directory on the filesystem, as provided by the user.</comment>
|
||||
</data>
|
||||
<data name="ElevationRequired" xml:space="preserve">
|
||||
<value>要求された操作には、権限の昇格が必要です。</value>
|
||||
</data>
|
||||
<data name="FileNotFound" xml:space="preserve">
|
||||
<value>指定されたファイルが見つかりません。</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -209,7 +209,7 @@
|
||||
</data>
|
||||
<data name="CtrlDToClose" xml:space="preserve">
|
||||
<value>이제 Ctrl+D 이 터미널을 닫거나 Enter 키를 눌러 다시 시작할 수 있습니다.</value>
|
||||
<comment>"Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter).</comment>
|
||||
<comment>"Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter).</comment>
|
||||
</data>
|
||||
<data name="ProcessFailedToLaunch" xml:space="preserve">
|
||||
<value>[’{1}' 시작 시 {0} 오류 발생]</value>
|
||||
@@ -220,4 +220,10 @@
|
||||
<value>시작 디렉터리 "{0}"에 액세스할 수 없습니다.</value>
|
||||
<comment>The first argument {0} is a path to a directory on the filesystem, as provided by the user.</comment>
|
||||
</data>
|
||||
<data name="ElevationRequired" xml:space="preserve">
|
||||
<value>요청한 작업을 수행하려면 권한 상승이 필요합니다.</value>
|
||||
</data>
|
||||
<data name="FileNotFound" xml:space="preserve">
|
||||
<value>시스템에서 지정된 파일을 찾을 수 없습니다.</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -209,7 +209,7 @@
|
||||
</data>
|
||||
<data name="CtrlDToClose" xml:space="preserve">
|
||||
<value>Agora você pode fechar este terminal com Ctrl+D ou pressione Enter para reiniciar.</value>
|
||||
<comment>"Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter).</comment>
|
||||
<comment>"Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter).</comment>
|
||||
</data>
|
||||
<data name="ProcessFailedToLaunch" xml:space="preserve">
|
||||
<value>[erro {0} ao iniciar "{1}"]</value>
|
||||
@@ -220,4 +220,10 @@
|
||||
<value>Não foi possível acessar o diretório inicial "{0}"</value>
|
||||
<comment>The first argument {0} is a path to a directory on the filesystem, as provided by the user.</comment>
|
||||
</data>
|
||||
<data name="ElevationRequired" xml:space="preserve">
|
||||
<value>A operação solicitada exige elevação.</value>
|
||||
</data>
|
||||
<data name="FileNotFound" xml:space="preserve">
|
||||
<value>O sistema não pode encontrar o arquivo especificado.</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -209,7 +209,7 @@
|
||||
</data>
|
||||
<data name="CtrlDToClose" xml:space="preserve">
|
||||
<value>Ỳóŭ ćǻή иòω сĺøѕè ťнįş тёѓмîήªŀ ωīťђ Çťѓℓ+Ď, όг ргéšѕ Σñтèř το ґèšтªят. !!! !!! !!! !!! !!! !!! !!!</value>
|
||||
<comment>"Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter).</comment>
|
||||
<comment>"Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter).</comment>
|
||||
</data>
|
||||
<data name="ProcessFailedToLaunch" xml:space="preserve">
|
||||
<value>[℮ѓѓŏŕ {0} ωĥєй łåύñćђίηğ `{1}'] !!! !!! !!! </value>
|
||||
@@ -220,4 +220,10 @@
|
||||
<value>Ċőŭľđ йōť ª¢čеѕş şτāŗťΐиğ ðιѓεςтоŗγ "{0}" !!! !!! !!! !!!</value>
|
||||
<comment>The first argument {0} is a path to a directory on the filesystem, as provided by the user.</comment>
|
||||
</data>
|
||||
</root>
|
||||
<data name="ElevationRequired" xml:space="preserve">
|
||||
<value>Ţнė ŗēqμĕѕŧєđ ôφ℮ґάтĩöй ŕєqΰїŗęś ėĺęνáτîøŋ. !!! !!! !!! !!! </value>
|
||||
</data>
|
||||
<data name="FileNotFound" xml:space="preserve">
|
||||
<value>Ŧнё şγśţêм ςâлηόť ƒїлď ŧнэ ƒΐĺë śрéćιƒієð. !!! !!! !!! !!! </value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -209,7 +209,7 @@
|
||||
</data>
|
||||
<data name="CtrlDToClose" xml:space="preserve">
|
||||
<value>Ỳóŭ ćǻή иòω сĺøѕè ťнįş тёѓмîήªŀ ωīťђ Çťѓℓ+Ď, όг ргéšѕ Σñтèř το ґèšтªят. !!! !!! !!! !!! !!! !!! !!!</value>
|
||||
<comment>"Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter).</comment>
|
||||
<comment>"Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter).</comment>
|
||||
</data>
|
||||
<data name="ProcessFailedToLaunch" xml:space="preserve">
|
||||
<value>[℮ѓѓŏŕ {0} ωĥєй łåύñćђίηğ `{1}'] !!! !!! !!! </value>
|
||||
@@ -220,4 +220,10 @@
|
||||
<value>Ċőŭľđ йōť ª¢čеѕş şτāŗťΐиğ ðιѓεςтоŗγ "{0}" !!! !!! !!! !!!</value>
|
||||
<comment>The first argument {0} is a path to a directory on the filesystem, as provided by the user.</comment>
|
||||
</data>
|
||||
</root>
|
||||
<data name="ElevationRequired" xml:space="preserve">
|
||||
<value>Ţнė ŗēqμĕѕŧєđ ôφ℮ґάтĩöй ŕєqΰїŗęś ėĺęνáτîøŋ. !!! !!! !!! !!! </value>
|
||||
</data>
|
||||
<data name="FileNotFound" xml:space="preserve">
|
||||
<value>Ŧнё şγśţêм ςâлηόť ƒїлď ŧнэ ƒΐĺë śрéćιƒієð. !!! !!! !!! !!! </value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -209,7 +209,7 @@
|
||||
</data>
|
||||
<data name="CtrlDToClose" xml:space="preserve">
|
||||
<value>Ỳóŭ ćǻή иòω сĺøѕè ťнįş тёѓмîήªŀ ωīťђ Çťѓℓ+Ď, όг ргéšѕ Σñтèř το ґèšтªят. !!! !!! !!! !!! !!! !!! !!!</value>
|
||||
<comment>"Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter).</comment>
|
||||
<comment>"Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter).</comment>
|
||||
</data>
|
||||
<data name="ProcessFailedToLaunch" xml:space="preserve">
|
||||
<value>[℮ѓѓŏŕ {0} ωĥєй łåύñćђίηğ `{1}'] !!! !!! !!! </value>
|
||||
@@ -220,4 +220,10 @@
|
||||
<value>Ċőŭľđ йōť ª¢čеѕş şτāŗťΐиğ ðιѓεςтоŗγ "{0}" !!! !!! !!! !!!</value>
|
||||
<comment>The first argument {0} is a path to a directory on the filesystem, as provided by the user.</comment>
|
||||
</data>
|
||||
</root>
|
||||
<data name="ElevationRequired" xml:space="preserve">
|
||||
<value>Ţнė ŗēqμĕѕŧєđ ôφ℮ґάтĩöй ŕєqΰїŗęś ėĺęνáτîøŋ. !!! !!! !!! !!! </value>
|
||||
</data>
|
||||
<data name="FileNotFound" xml:space="preserve">
|
||||
<value>Ŧнё şγśţêм ςâлηόť ƒїлď ŧнэ ƒΐĺë śрéćιƒієð. !!! !!! !!! !!! </value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -209,7 +209,7 @@
|
||||
</data>
|
||||
<data name="CtrlDToClose" xml:space="preserve">
|
||||
<value>Теперь вы можете закрыть этот терминал с помощью клавиш CTRL+D. Или нажмите клавишу ВВОД для перезапуска.</value>
|
||||
<comment>"Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter).</comment>
|
||||
<comment>"Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter).</comment>
|
||||
</data>
|
||||
<data name="ProcessFailedToLaunch" xml:space="preserve">
|
||||
<value>[ошибка {0} при запуске "{1}"]</value>
|
||||
@@ -220,4 +220,10 @@
|
||||
<value>Не удалось получить доступ к запуску каталога "{0}"</value>
|
||||
<comment>The first argument {0} is a path to a directory on the filesystem, as provided by the user.</comment>
|
||||
</data>
|
||||
<data name="ElevationRequired" xml:space="preserve">
|
||||
<value>Запрошенная операция требует получения дополнительных прав.</value>
|
||||
</data>
|
||||
<data name="FileNotFound" xml:space="preserve">
|
||||
<value>Не удается найти указанный файл.</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -209,7 +209,7 @@
|
||||
</data>
|
||||
<data name="CtrlDToClose" xml:space="preserve">
|
||||
<value>现在可以使用Ctrl+D关闭此终端,或按 Enter 重新启动。</value>
|
||||
<comment>"Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter).</comment>
|
||||
<comment>"Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter).</comment>
|
||||
</data>
|
||||
<data name="ProcessFailedToLaunch" xml:space="preserve">
|
||||
<value>[出现错误 {0} (启动“{1}”时)]</value>
|
||||
@@ -220,4 +220,10 @@
|
||||
<value>无法访问启动目录“{0}”</value>
|
||||
<comment>The first argument {0} is a path to a directory on the filesystem, as provided by the user.</comment>
|
||||
</data>
|
||||
<data name="ElevationRequired" xml:space="preserve">
|
||||
<value>请求的操作需要提升。</value>
|
||||
</data>
|
||||
<data name="FileNotFound" xml:space="preserve">
|
||||
<value>系统找不到指定的文件。</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -209,7 +209,7 @@
|
||||
</data>
|
||||
<data name="CtrlDToClose" xml:space="preserve">
|
||||
<value>您現在可以使用 Ctrl+D 關閉此終端機,或按 Enter 重新啟動。</value>
|
||||
<comment>"Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter).</comment>
|
||||
<comment>"Ctrl+D" and "Enter" represent keys the user will press (control+D and Enter).</comment>
|
||||
</data>
|
||||
<data name="ProcessFailedToLaunch" xml:space="preserve">
|
||||
<value>[啟動 `{1}' 時發生錯誤 {0}]</value>
|
||||
@@ -220,4 +220,10 @@
|
||||
<value>無法存取開始目錄 "{0}"</value>
|
||||
<comment>The first argument {0} is a path to a directory on the filesystem, as provided by the user.</comment>
|
||||
</data>
|
||||
<data name="ElevationRequired" xml:space="preserve">
|
||||
<value>要求的操作需要提高權限。</value>
|
||||
</data>
|
||||
<data name="FileNotFound" xml:space="preserve">
|
||||
<value>系統找不到指定的檔案。</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -142,23 +142,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// If we wait, a screen reader may try to get the AutomationPeer (aka the UIA Engine), and we won't be able to attach
|
||||
// the UIA Engine to the renderer. This prevents us from signaling changes to the cursor or buffer.
|
||||
{
|
||||
// First create the render thread.
|
||||
// Then stash a local pointer to the render thread so we can initialize it and enable it
|
||||
// to paint itself *after* we hand off its ownership to the renderer.
|
||||
// We split up construction and initialization of the render thread object this way
|
||||
// because the renderer and render thread have circular references to each other.
|
||||
auto renderThread = std::make_unique<::Microsoft::Console::Render::RenderThread>();
|
||||
auto* const localPointerToThread = renderThread.get();
|
||||
|
||||
// Now create the renderer and initialize the render thread.
|
||||
const auto& renderSettings = _terminal->GetRenderSettings();
|
||||
_renderer = std::make_unique<::Microsoft::Console::Render::Renderer>(renderSettings, _terminal.get(), nullptr, 0, std::move(renderThread));
|
||||
_renderer = std::make_unique<::Microsoft::Console::Render::Renderer>(renderSettings, _terminal.get());
|
||||
|
||||
_renderer->SetBackgroundColorChangedCallback([this]() { _rendererBackgroundColorChanged(); });
|
||||
_renderer->SetFrameColorChangedCallback([this]() { _rendererTabColorChanged(); });
|
||||
_renderer->SetRendererEnteredErrorStateCallback([this]() { RendererEnteredErrorState.raise(nullptr, nullptr); });
|
||||
|
||||
THROW_IF_FAILED(localPointerToThread->Initialize(_renderer.get()));
|
||||
}
|
||||
|
||||
UpdateSettings(settings, unfocusedAppearance);
|
||||
@@ -186,7 +176,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// thread is a workaround for us to hit GH#12607 less often.
|
||||
shared->outputIdle = std::make_unique<til::debounced_func_trailing<>>(
|
||||
std::chrono::milliseconds{ 100 },
|
||||
[weakTerminal = std::weak_ptr{ _terminal }, weakThis = get_weak(), dispatcher = _dispatcher]() {
|
||||
[this, weakThis = get_weak(), dispatcher = _dispatcher]() {
|
||||
dispatcher.TryEnqueue(DispatcherQueuePriority::Normal, [weakThis]() {
|
||||
if (const auto self = weakThis.get(); self && !self->_IsClosing())
|
||||
{
|
||||
@@ -194,22 +184,23 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
});
|
||||
|
||||
if (const auto t = weakTerminal.lock())
|
||||
{
|
||||
const auto lock = t->LockForWriting();
|
||||
t->UpdatePatternsUnderLock();
|
||||
}
|
||||
// We can't use a `weak_ptr` to `_terminal` here, because it takes significant
|
||||
// dependency on the lifetime of `this` (primarily on our `_renderer`).
|
||||
// and a `weak_ptr` would allow it to outlive `this`.
|
||||
// Theoretically `debounced_func_trailing` should call `WaitForThreadpoolTimerCallbacks()`
|
||||
// with cancel=true on destruction, which should ensure that our use of `this` here is safe.
|
||||
const auto lock = _terminal->LockForWriting();
|
||||
_terminal->UpdatePatternsUnderLock();
|
||||
});
|
||||
|
||||
// If you rapidly show/hide Windows Terminal, something about GotFocus()/LostFocus() gets broken.
|
||||
// We'll then receive easily 10+ such calls from WinUI the next time the application is shown.
|
||||
shared->focusChanged = std::make_unique<til::debounced_func_trailing<bool>>(
|
||||
std::chrono::milliseconds{ 25 },
|
||||
[weakThis = get_weak()](const bool focused) {
|
||||
if (const auto core{ weakThis.get() })
|
||||
{
|
||||
core->_focusChanged(focused);
|
||||
}
|
||||
[this](const bool focused) {
|
||||
// Theoretically `debounced_func_trailing` should call `WaitForThreadpoolTimerCallbacks()`
|
||||
// with cancel=true on destruction, which should ensure that our use of `this` here is safe.
|
||||
_focusChanged(focused);
|
||||
});
|
||||
|
||||
// Scrollbar updates are also expensive (XAML), so we'll throttle them as well.
|
||||
@@ -224,19 +215,35 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
});
|
||||
}
|
||||
|
||||
// Safely disconnects event handlers from the connection and closes it. This is necessary because
|
||||
// WinRT event revokers don't prevent pending calls from proceeding (thread-safe but not race-free).
|
||||
void ControlCore::_closeConnection()
|
||||
{
|
||||
_connectionOutputEventRevoker.revoke();
|
||||
_connectionStateChangedRevoker.revoke();
|
||||
|
||||
// One of the tasks for `ITerminalConnection::Close()` is to block until all pending
|
||||
// callback calls have completed. This solves the race-condition issue mentioned above.
|
||||
if (_connection)
|
||||
{
|
||||
_connection.Close();
|
||||
_connection = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ControlCore::~ControlCore()
|
||||
{
|
||||
Close();
|
||||
|
||||
_renderer.reset();
|
||||
_renderEngine.reset();
|
||||
// See notes about the _renderer member in the header file.
|
||||
_renderer->TriggerTeardown();
|
||||
}
|
||||
|
||||
void ControlCore::Detach()
|
||||
{
|
||||
// Disable the renderer, so that it doesn't try to start any new frames
|
||||
// for our engines while we're not attached to anything.
|
||||
_renderer->WaitForPaintCompletionAndDisable(INFINITE);
|
||||
_renderer->TriggerTeardown();
|
||||
|
||||
// Clear out any throttled funcs that we had wired up to run on this UI
|
||||
// thread. These will be recreated in _setupDispatcherAndCallbacks, when
|
||||
@@ -276,8 +283,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
auto oldState = ConnectionState(); // rely on ControlCore's automatic null handling
|
||||
// revoke ALL old handlers immediately
|
||||
|
||||
_connectionOutputEventRevoker.revoke();
|
||||
_connectionStateChangedRevoker.revoke();
|
||||
_closeConnection();
|
||||
|
||||
_connection = newConnection;
|
||||
if (_connection)
|
||||
@@ -366,7 +372,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
const auto vp = _renderEngine->GetViewportInCharacters(viewInPixels);
|
||||
const auto width = vp.Width();
|
||||
const auto height = vp.Height();
|
||||
_connection.Resize(height, width);
|
||||
|
||||
if (_connection)
|
||||
{
|
||||
_connection.Resize(height, width);
|
||||
}
|
||||
|
||||
if (_owningHwnd != 0)
|
||||
{
|
||||
@@ -420,6 +430,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
if (_initializedTerminal.load(std::memory_order_relaxed))
|
||||
{
|
||||
// The lock must be held, because it calls into IRenderData which is shared state.
|
||||
const auto lock = _terminal->LockForWriting();
|
||||
_renderer->EnablePainting();
|
||||
}
|
||||
@@ -434,7 +445,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// - <none>
|
||||
void ControlCore::_sendInputToConnection(std::wstring_view wstr)
|
||||
{
|
||||
_connection.WriteInput(winrt_wstring_to_array_view(wstr));
|
||||
if (_connection)
|
||||
{
|
||||
_connection.WriteInput(winrt_wstring_to_array_view(wstr));
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -471,7 +485,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
const wchar_t CtrlD = 0x4;
|
||||
const wchar_t Enter = '\r';
|
||||
|
||||
if (_connection.State() >= winrt::Microsoft::Terminal::TerminalConnection::ConnectionState::Closed)
|
||||
if (_connection && _connection.State() >= winrt::Microsoft::Terminal::TerminalConnection::ConnectionState::Closed)
|
||||
{
|
||||
if (ch == CtrlD)
|
||||
{
|
||||
@@ -1122,7 +1136,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
return;
|
||||
}
|
||||
|
||||
_connection.Resize(vp.Height(), vp.Width());
|
||||
if (_connection)
|
||||
{
|
||||
_connection.Resize(vp.Height(), vp.Width());
|
||||
}
|
||||
|
||||
// TermControl will call Search() once the OutputIdle even fires after 100ms.
|
||||
// Until then we need to hide the now-stale search results from the renderer.
|
||||
@@ -1794,12 +1811,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
// Ensure Close() doesn't hang, waiting for MidiAudio to finish playing an hour long song.
|
||||
_midiAudio.BeginSkip();
|
||||
|
||||
// Stop accepting new output and state changes before we disconnect everything.
|
||||
_connectionOutputEventRevoker.revoke();
|
||||
_connectionStateChangedRevoker.revoke();
|
||||
_connection.Close();
|
||||
}
|
||||
|
||||
_closeConnection();
|
||||
}
|
||||
|
||||
void ControlCore::PersistToPath(const wchar_t* path) const
|
||||
@@ -1817,9 +1831,23 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
|
||||
FILETIME lastWriteTime;
|
||||
FILETIME localFileTime;
|
||||
SYSTEMTIME lastWriteSystemTime;
|
||||
if (!GetFileTime(file.get(), nullptr, nullptr, &lastWriteTime) ||
|
||||
!FileTimeToSystemTime(&lastWriteTime, &lastWriteSystemTime))
|
||||
|
||||
// Get the last write time in UTC
|
||||
if (!GetFileTime(file.get(), nullptr, nullptr, &lastWriteTime))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert UTC FILETIME to local FILETIME
|
||||
if (!FileTimeToLocalFileTime(&lastWriteTime, &localFileTime))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert local FILETIME to SYSTEMTIME
|
||||
if (!FileTimeToSystemTime(&localFileTime, &lastWriteSystemTime))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -1896,7 +1924,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
const auto weakThis{ get_weak() };
|
||||
|
||||
// Concurrent read of _dispatcher is safe, because Detach() calls WaitForPaintCompletionAndDisable()
|
||||
// Concurrent read of _dispatcher is safe, because Detach() calls TriggerTeardown()
|
||||
// which blocks until this call returns. _dispatcher will only be changed afterwards.
|
||||
co_await wil::resume_foreground(_dispatcher);
|
||||
|
||||
@@ -1947,8 +1975,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
void ControlCore::ResumeRendering()
|
||||
{
|
||||
// The lock must be held, because it calls into IRenderData which is shared state.
|
||||
const auto lock = _terminal->LockForWriting();
|
||||
_renderer->ResetErrorStateAndResume();
|
||||
_renderer->EnablePainting();
|
||||
}
|
||||
|
||||
bool ControlCore::IsVtMouseModeEnabled() const
|
||||
@@ -2839,6 +2868,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// coloring other matches, then we need to make sure those get redrawn,
|
||||
// too.
|
||||
_renderer->TriggerRedrawAll();
|
||||
_updateSelectionUI();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,65 +311,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
std::shared_ptr<ThrottledFuncTrailing<Control::ScrollPositionChangedArgs>> updateScrollBar;
|
||||
};
|
||||
|
||||
std::atomic<bool> _initializedTerminal{ false };
|
||||
bool _closing{ false };
|
||||
|
||||
TerminalConnection::ITerminalConnection _connection{ nullptr };
|
||||
TerminalConnection::ITerminalConnection::TerminalOutput_revoker _connectionOutputEventRevoker;
|
||||
TerminalConnection::ITerminalConnection::StateChanged_revoker _connectionStateChangedRevoker;
|
||||
|
||||
winrt::com_ptr<ControlSettings> _settings{ nullptr };
|
||||
|
||||
std::shared_ptr<::Microsoft::Terminal::Core::Terminal> _terminal{ nullptr };
|
||||
std::wstring _pendingResponses;
|
||||
|
||||
// NOTE: _renderEngine must be ordered before _renderer.
|
||||
//
|
||||
// As _renderer has a dependency on _renderEngine (through a raw pointer)
|
||||
// we must ensure the _renderer is deallocated first.
|
||||
// (C++ class members are destroyed in reverse order.)
|
||||
std::unique_ptr<::Microsoft::Console::Render::Atlas::AtlasEngine> _renderEngine{ nullptr };
|
||||
std::unique_ptr<::Microsoft::Console::Render::Renderer> _renderer{ nullptr };
|
||||
|
||||
::Search _searcher;
|
||||
bool _snapSearchResultToSelection;
|
||||
|
||||
winrt::handle _lastSwapChainHandle{ nullptr };
|
||||
|
||||
FontInfoDesired _desiredFont;
|
||||
FontInfo _actualFont;
|
||||
bool _builtinGlyphs = true;
|
||||
bool _colorGlyphs = true;
|
||||
CSSLengthPercentage _cellWidth;
|
||||
CSSLengthPercentage _cellHeight;
|
||||
|
||||
// storage location for the leading surrogate of a utf-16 surrogate pair
|
||||
std::optional<wchar_t> _leadingSurrogate{ std::nullopt };
|
||||
|
||||
std::optional<til::point> _lastHoveredCell{ std::nullopt };
|
||||
// Track the last hyperlink ID we hovered over
|
||||
uint16_t _lastHoveredId{ 0 };
|
||||
|
||||
bool _isReadOnly{ false };
|
||||
|
||||
std::optional<interval_tree::IntervalTree<til::point, size_t>::interval> _lastHoveredInterval{ std::nullopt };
|
||||
|
||||
// These members represent the size of the surface that we should be
|
||||
// rendering to.
|
||||
float _panelWidth{ 0 };
|
||||
float _panelHeight{ 0 };
|
||||
float _compositionScale{ 0 };
|
||||
|
||||
uint64_t _owningHwnd{ 0 };
|
||||
|
||||
winrt::Windows::System::DispatcherQueue _dispatcher{ nullptr };
|
||||
til::shared_mutex<SharedState> _shared;
|
||||
|
||||
til::point _contextMenuBufferPosition{ 0, 0 };
|
||||
|
||||
Windows::Foundation::Collections::IVector<hstring> _cachedQuickFixes{ nullptr };
|
||||
|
||||
void _setupDispatcherAndCallbacks();
|
||||
void _closeConnection();
|
||||
|
||||
bool _setFontSizeUnderLock(float fontSize);
|
||||
void _updateFont();
|
||||
@@ -396,12 +339,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void _terminalWindowSizeChanged(int32_t width, int32_t height);
|
||||
|
||||
safe_void_coroutine _terminalCompletionsChanged(std::wstring_view menuJson, unsigned int replaceLength);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
MidiAudio _midiAudio;
|
||||
winrt::Windows::System::DispatcherQueueTimer _midiAudioSkipTimer{ nullptr };
|
||||
|
||||
#pragma region RendererCallbacks
|
||||
void _rendererWarning(const HRESULT hr, wil::zwstring_view parameter);
|
||||
safe_void_coroutine _renderEngineSwapChainChanged(const HANDLE handle);
|
||||
@@ -412,6 +351,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void _raiseReadOnlyWarning();
|
||||
void _updateAntiAliasingMode();
|
||||
void _connectionOutputHandler(const hstring& hstr);
|
||||
void _connectionStateChangedHandler(const TerminalConnection::ITerminalConnection&, const Windows::Foundation::IInspectable&);
|
||||
void _updateHoveredCell(const std::optional<til::point> terminalPosition);
|
||||
void _setOpacity(const float opacity, const bool focused = true);
|
||||
|
||||
@@ -443,6 +383,70 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
return _closing;
|
||||
}
|
||||
|
||||
// Caches responses generated by our VT parser (= improved batching).
|
||||
std::wstring _pendingResponses;
|
||||
|
||||
// Font stuff.
|
||||
FontInfoDesired _desiredFont;
|
||||
FontInfo _actualFont;
|
||||
bool _builtinGlyphs = true;
|
||||
bool _colorGlyphs = true;
|
||||
CSSLengthPercentage _cellWidth;
|
||||
CSSLengthPercentage _cellHeight;
|
||||
|
||||
// Rendering stuff.
|
||||
winrt::handle _lastSwapChainHandle{ nullptr };
|
||||
uint64_t _owningHwnd{ 0 };
|
||||
float _panelWidth{ 0 };
|
||||
float _panelHeight{ 0 };
|
||||
float _compositionScale{ 0 };
|
||||
|
||||
// Audio stuff.
|
||||
MidiAudio _midiAudio;
|
||||
winrt::Windows::System::DispatcherQueueTimer _midiAudioSkipTimer{ nullptr };
|
||||
|
||||
// Other stuff.
|
||||
winrt::Windows::System::DispatcherQueue _dispatcher{ nullptr };
|
||||
winrt::com_ptr<ControlSettings> _settings{ nullptr };
|
||||
til::point _contextMenuBufferPosition{ 0, 0 };
|
||||
Windows::Foundation::Collections::IVector<hstring> _cachedQuickFixes{ nullptr };
|
||||
::Search _searcher;
|
||||
std::optional<interval_tree::IntervalTree<til::point, size_t>::interval> _lastHoveredInterval;
|
||||
std::optional<wchar_t> _leadingSurrogate;
|
||||
std::optional<til::point> _lastHoveredCell;
|
||||
uint16_t _lastHoveredId{ 0 };
|
||||
std::atomic<bool> _initializedTerminal{ false };
|
||||
bool _isReadOnly{ false };
|
||||
bool _closing{ false };
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// These are ordered last to ensure they're destroyed first.
|
||||
// This ensures that their respective contents stops taking dependency on the above.
|
||||
// I recommend reading the following paragraphs in reverse order.
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
// ↑ This one is tricky - all of these are raw pointers:
|
||||
// 1. _terminal depends on _renderer (for invalidations)
|
||||
// 2. _renderer depends on _terminal (for IRenderData)
|
||||
// = circular dependency = architectural flaw (lifetime issues) = TODO
|
||||
// 3. _renderer depends on _renderEngine (AtlasEngine)
|
||||
// To solve the knot, we manually stop the renderer in the destructor,
|
||||
// which breaks 2. We can proceed then proceed to break 1. and then 3.
|
||||
std::unique_ptr<::Microsoft::Console::Render::Atlas::AtlasEngine> _renderEngine{ nullptr }; // 3.
|
||||
std::unique_ptr<::Microsoft::Console::Render::Renderer> _renderer{ nullptr }; // 3.
|
||||
std::shared_ptr<::Microsoft::Terminal::Core::Terminal> _terminal{ nullptr }; // 1.
|
||||
|
||||
// ↑ MOST IMPORTANTLY: `_outputIdle` takes dependency on the raw `this` pointer (necessarily).
|
||||
// Destroying SharedState here will block until all pending `debounced_func_trailing` calls are completed.
|
||||
til::shared_mutex<SharedState> _shared;
|
||||
|
||||
// ↑ Prevent any more unnecessary `_outputIdle` calls.
|
||||
// Technically none of these members are destroyed here. Instead, the destructor will call Close()
|
||||
// which calls _closeConnection() which in turn manually & safely destroys them in the correct order.
|
||||
TerminalConnection::ITerminalConnection::TerminalOutput_revoker _connectionOutputEventRevoker;
|
||||
TerminalConnection::ITerminalConnection::StateChanged_revoker _connectionStateChangedRevoker;
|
||||
TerminalConnection::ITerminalConnection _connection{ nullptr };
|
||||
|
||||
friend class ControlUnitTests::ControlCoreTests;
|
||||
friend class ControlUnitTests::ControlInteractivityTests;
|
||||
bool _inUnitTests{ false };
|
||||
|
||||
@@ -73,7 +73,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
//
|
||||
// To alleviate, make sure to disable the UIA engine and remove it,
|
||||
// and ALSO disable the renderer. Core.Detach will take care of the
|
||||
// WaitForPaintCompletionAndDisable (which will stop the renderer
|
||||
// TriggerTeardown (which will stop the renderer
|
||||
// after all current engines are done painting).
|
||||
//
|
||||
// Simply disabling the UIA engine is not enough, because it's
|
||||
@@ -340,7 +340,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
const Core::Point pixelPosition,
|
||||
const bool pointerPressedInBounds)
|
||||
{
|
||||
const auto terminalPosition = _getTerminalPosition(til::point{ pixelPosition }, true);
|
||||
const auto terminalPosition = _getTerminalPosition(til::point{ pixelPosition }, false);
|
||||
// Returning true from this function indicates that the caller should do no further processing of this movement.
|
||||
bool handledCompletely = false;
|
||||
|
||||
@@ -489,7 +489,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
const Core::Point pixelPosition,
|
||||
const Control::MouseButtonState buttonState)
|
||||
{
|
||||
const auto terminalPosition = _getTerminalPosition(til::point{ pixelPosition }, true);
|
||||
const auto terminalPosition = _getTerminalPosition(til::point{ pixelPosition }, false);
|
||||
|
||||
// Short-circuit isReadOnly check to avoid warning dialog.
|
||||
//
|
||||
|
||||
@@ -19,6 +19,79 @@ using namespace ::Microsoft::Terminal::Core;
|
||||
|
||||
static LPCWSTR term_window_class = L"HwndTerminalClass";
|
||||
|
||||
STDMETHODIMP HwndTerminal::TsfDataProvider::QueryInterface(REFIID, void**) noexcept
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE HwndTerminal::TsfDataProvider::AddRef() noexcept
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE HwndTerminal::TsfDataProvider::Release() noexcept
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
HWND HwndTerminal::TsfDataProvider::GetHwnd()
|
||||
{
|
||||
return _terminal->GetHwnd();
|
||||
}
|
||||
|
||||
RECT HwndTerminal::TsfDataProvider::GetViewport()
|
||||
{
|
||||
const auto hwnd = GetHwnd();
|
||||
|
||||
RECT rc;
|
||||
GetClientRect(hwnd, &rc);
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getclientrect
|
||||
// > The left and top members are zero. The right and bottom members contain the width and height of the window.
|
||||
// --> We can turn the client rect into a screen-relative rect by adding the left/top position.
|
||||
ClientToScreen(hwnd, reinterpret_cast<POINT*>(&rc));
|
||||
rc.right += rc.left;
|
||||
rc.bottom += rc.top;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
RECT HwndTerminal::TsfDataProvider::GetCursorPosition()
|
||||
{
|
||||
// Convert from columns/rows to pixels.
|
||||
til::point cursorPos;
|
||||
til::size fontSize;
|
||||
{
|
||||
const auto lock = _terminal->_terminal->LockForReading();
|
||||
cursorPos = _terminal->_terminal->GetCursorPosition(); // measured in terminal cells
|
||||
fontSize = _terminal->_actualFont.GetSize(); // measured in pixels, not DIP
|
||||
}
|
||||
POINT ptSuggestion = {
|
||||
.x = cursorPos.x * fontSize.width,
|
||||
.y = cursorPos.y * fontSize.height,
|
||||
};
|
||||
|
||||
ClientToScreen(GetHwnd(), &ptSuggestion);
|
||||
|
||||
// Final measurement should be in pixels
|
||||
return {
|
||||
.left = ptSuggestion.x,
|
||||
.top = ptSuggestion.y,
|
||||
.right = ptSuggestion.x + fontSize.width,
|
||||
.bottom = ptSuggestion.y + fontSize.height,
|
||||
};
|
||||
}
|
||||
|
||||
void HwndTerminal::TsfDataProvider::HandleOutput(std::wstring_view text)
|
||||
{
|
||||
_terminal->_WriteTextToConnection(text);
|
||||
}
|
||||
|
||||
Microsoft::Console::Render::Renderer* HwndTerminal::TsfDataProvider::GetRenderer()
|
||||
{
|
||||
return _terminal->_renderer.get();
|
||||
}
|
||||
|
||||
// This magic flag is "documented" at https://msdn.microsoft.com/en-us/library/windows/desktop/ms646301(v=vs.85).aspx
|
||||
// "If the high-order bit is 1, the key is down; otherwise, it is up."
|
||||
static constexpr short KeyPressed{ gsl::narrow_cast<short>(0x8000) };
|
||||
@@ -206,14 +279,10 @@ HRESULT HwndTerminal::Initialize()
|
||||
_terminal = std::make_unique<::Microsoft::Terminal::Core::Terminal>();
|
||||
const auto lock = _terminal->LockForWriting();
|
||||
|
||||
auto renderThread = std::make_unique<::Microsoft::Console::Render::RenderThread>();
|
||||
auto* const localPointerToThread = renderThread.get();
|
||||
auto& renderSettings = _terminal->GetRenderSettings();
|
||||
renderSettings.SetColorTableEntry(TextColor::DEFAULT_BACKGROUND, RGB(12, 12, 12));
|
||||
renderSettings.SetColorTableEntry(TextColor::DEFAULT_FOREGROUND, RGB(204, 204, 204));
|
||||
_renderer = std::make_unique<::Microsoft::Console::Render::Renderer>(renderSettings, _terminal.get(), nullptr, 0, std::move(renderThread));
|
||||
RETURN_HR_IF_NULL(E_POINTER, localPointerToThread);
|
||||
RETURN_IF_FAILED(localPointerToThread->Initialize(_renderer.get()));
|
||||
_renderer = std::make_unique<::Microsoft::Console::Render::Renderer>(renderSettings, _terminal.get());
|
||||
|
||||
auto engine = std::make_unique<::Microsoft::Console::Render::AtlasEngine>();
|
||||
RETURN_IF_FAILED(engine->SetHwnd(_hwnd.get()));
|
||||
@@ -234,7 +303,8 @@ HRESULT HwndTerminal::Initialize()
|
||||
|
||||
_terminal->Create({ 80, 25 }, 9001, *_renderer);
|
||||
_terminal->SetWriteInputCallback([=](std::wstring_view input) noexcept { _WriteTextToConnection(input); });
|
||||
localPointerToThread->EnablePainting();
|
||||
_terminal->SetCopyToClipboardCallback([=](wil::zwstring_view text) noexcept { _CopyTextToSystemClipboard(text, {}, {}); });
|
||||
_renderer->EnablePainting();
|
||||
|
||||
_multiClickTime = std::chrono::milliseconds{ GetDoubleClickTime() };
|
||||
|
||||
@@ -246,11 +316,16 @@ try
|
||||
{
|
||||
// As a rule, detach resources from the Terminal before shutting them down.
|
||||
// This ensures that teardown is reentrant.
|
||||
_tsfHandle = {};
|
||||
|
||||
// Shut down the renderer (and therefore the thread) before we implode
|
||||
_renderer.reset();
|
||||
_renderEngine.reset();
|
||||
|
||||
// These two callbacks have a dangling reference to `this`; let's just clear them
|
||||
_terminal->SetWriteInputCallback(nullptr);
|
||||
_terminal->SetCopyToClipboardCallback(nullptr);
|
||||
|
||||
if (auto localHwnd{ _hwnd.release() })
|
||||
{
|
||||
// If we're being called through WM_DESTROY, we won't get here (hwnd is already released)
|
||||
@@ -897,6 +972,7 @@ void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR font
|
||||
publicTerminal->_terminal->SetCursorStyle(static_cast<Microsoft::Console::VirtualTerminal::DispatchTypes::CursorStyle>(theme.CursorStyle));
|
||||
|
||||
publicTerminal->_desiredFont = { fontFamily, 0, DEFAULT_FONT_WEIGHT, static_cast<float>(fontSize), CP_UTF8 };
|
||||
publicTerminal->_desiredFont.SetEnableBuiltinGlyphs(true);
|
||||
publicTerminal->_UpdateFont(newDpi);
|
||||
}
|
||||
|
||||
@@ -945,6 +1021,16 @@ void __stdcall TerminalSetFocus(void* terminal)
|
||||
{
|
||||
LOG_IF_FAILED(uiaEngine->Enable());
|
||||
}
|
||||
publicTerminal->_FocusTSF();
|
||||
}
|
||||
|
||||
void HwndTerminal::_FocusTSF() noexcept
|
||||
{
|
||||
if (!_tsfHandle)
|
||||
{
|
||||
_tsfHandle = Microsoft::Console::TSF::Handle::Create();
|
||||
_tsfHandle.AssociateFocus(&_tsfDataProvider);
|
||||
}
|
||||
}
|
||||
|
||||
void __stdcall TerminalKillFocus(void* terminal)
|
||||
@@ -963,7 +1049,7 @@ void __stdcall TerminalKillFocus(void* terminal)
|
||||
// - text - selected text in plain-text format
|
||||
// - htmlData - selected text in HTML format
|
||||
// - rtfData - selected text in RTF format
|
||||
HRESULT HwndTerminal::_CopyTextToSystemClipboard(const std::wstring& text, const std::string& htmlData, const std::string& rtfData) const
|
||||
HRESULT HwndTerminal::_CopyTextToSystemClipboard(wil::zwstring_view text, wil::zstring_view htmlData, wil::zstring_view rtfData) const
|
||||
try
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_NOT_VALID_STATE, _terminal);
|
||||
@@ -1019,7 +1105,7 @@ CATCH_RETURN()
|
||||
// Arguments:
|
||||
// - stringToCopy - The string to copy
|
||||
// - lpszFormat - the name of the format
|
||||
HRESULT HwndTerminal::_CopyToSystemClipboard(const std::string& stringToCopy, LPCWSTR lpszFormat) const
|
||||
HRESULT HwndTerminal::_CopyToSystemClipboard(wil::zstring_view stringToCopy, LPCWSTR lpszFormat) const
|
||||
{
|
||||
const auto cbData = stringToCopy.size() + 1; // +1 for '\0'
|
||||
if (cbData)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "../../buffer/out/textBuffer.hpp"
|
||||
#include "../../renderer/inc/FontInfoDesired.hpp"
|
||||
#include "../../types/IControlAccessibilityInfo.h"
|
||||
#include "../../tsf/Handle.h"
|
||||
|
||||
namespace Microsoft::Console::Render::Atlas
|
||||
{
|
||||
@@ -85,6 +86,21 @@ public:
|
||||
static LRESULT CALLBACK HwndTerminalWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) noexcept;
|
||||
|
||||
private:
|
||||
struct TsfDataProvider : public Microsoft::Console::TSF::IDataProvider
|
||||
{
|
||||
TsfDataProvider(HwndTerminal* t) :
|
||||
_terminal(t) {}
|
||||
virtual ~TsfDataProvider() = default;
|
||||
STDMETHODIMP TsfDataProvider::QueryInterface(REFIID, void**) noexcept override;
|
||||
ULONG STDMETHODCALLTYPE TsfDataProvider::AddRef() noexcept override;
|
||||
ULONG STDMETHODCALLTYPE TsfDataProvider::Release() noexcept override;
|
||||
HWND GetHwnd() override;
|
||||
RECT GetViewport() override;
|
||||
RECT GetCursorPosition() override;
|
||||
void HandleOutput(std::wstring_view text) override;
|
||||
Microsoft::Console::Render::Renderer* GetRenderer() override;
|
||||
HwndTerminal* _terminal;
|
||||
};
|
||||
wil::unique_hwnd _hwnd;
|
||||
FontInfoDesired _desiredFont;
|
||||
FontInfo _actualFont;
|
||||
@@ -106,6 +122,10 @@ private:
|
||||
std::optional<til::point> _lastMouseClickPos;
|
||||
std::optional<til::point> _singleClickTouchdownPos;
|
||||
|
||||
// _tsfHandle uses _tsfDataProvider. Destructors run from bottom to top; this maintains correct destruction order.
|
||||
TsfDataProvider _tsfDataProvider{ this };
|
||||
Microsoft::Console::TSF::Handle _tsfHandle;
|
||||
|
||||
friend HRESULT _stdcall CreateTerminal(HWND parentHwnd, _Out_ void** hwnd, _Out_ void** terminal);
|
||||
friend HRESULT _stdcall TerminalTriggerResize(_In_ void* terminal, _In_ til::CoordType width, _In_ til::CoordType height, _Out_ til::size* dimensions);
|
||||
friend HRESULT _stdcall TerminalTriggerResizeWithDimension(_In_ void* terminal, _In_ til::size dimensions, _Out_ til::size* dimensionsInPixels);
|
||||
@@ -125,10 +145,12 @@ private:
|
||||
|
||||
void _UpdateFont(int newDpi);
|
||||
void _WriteTextToConnection(const std::wstring_view text) noexcept;
|
||||
HRESULT _CopyTextToSystemClipboard(const std::wstring& text, const std::string& htmlData, const std::string& rtfData) const;
|
||||
HRESULT _CopyToSystemClipboard(const std::string& stringToCopy, LPCWSTR lpszFormat) const;
|
||||
HRESULT _CopyTextToSystemClipboard(wil::zwstring_view text, wil::zstring_view htmlData, wil::zstring_view rtfData) const;
|
||||
HRESULT _CopyToSystemClipboard(wil::zstring_view stringToCopy, LPCWSTR lpszFormat) const;
|
||||
void _PasteTextFromClipboard() noexcept;
|
||||
|
||||
void _FocusTSF() noexcept;
|
||||
|
||||
const unsigned int _NumberOfClicks(til::point clickPos, std::chrono::steady_clock::time_point clickTime) noexcept;
|
||||
HRESULT _StartSelection(LPARAM lParam) noexcept;
|
||||
HRESULT _MoveSelection(LPARAM lParam) noexcept;
|
||||
|
||||
@@ -118,7 +118,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
return WrapArrayOfTextRangeProviders(pReturnVal);
|
||||
}
|
||||
|
||||
XamlAutomation::ITextRangeProvider InteractivityAutomationPeer::RangeFromChild(XamlAutomation::IRawElementProviderSimple childElement)
|
||||
XamlAutomation::ITextRangeProvider InteractivityAutomationPeer::RangeFromChild(XamlAutomation::IRawElementProviderSimple /*childElement*/)
|
||||
{
|
||||
UIA::ITextRangeProvider* returnVal;
|
||||
// ScreenInfoUiaProvider doesn't actually use parameter, so just pass in nullptr
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
<ItemDefinitionGroup>
|
||||
<Link>
|
||||
<AdditionalDependencies>delayimp.lib;Uiautomationcore.lib;onecoreuap.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<DelayLoadDLLs>uiautomationcore.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
|
||||
<DelayLoadDLLs>uiautomationcore.dll;icu.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
|
||||
<!--
|
||||
ControlLib contains a DllMain that we need to force the use of.
|
||||
If you don't have this, then you'll see an error like
|
||||
|
||||
@@ -93,7 +93,7 @@ void Terminal::UpdateSettings(ICoreSettings settings)
|
||||
|
||||
if (_stateMachine)
|
||||
{
|
||||
SetVtChecksumReportSupport(settings.AllowVtChecksumReport());
|
||||
SetOptionalFeatures(settings);
|
||||
}
|
||||
|
||||
_getTerminalInput().ForceDisableWin32InputMode(settings.ForceVTInput());
|
||||
@@ -218,10 +218,13 @@ void Terminal::SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle)
|
||||
engine.Dispatch().SetCursorStyle(cursorStyle);
|
||||
}
|
||||
|
||||
void Terminal::SetVtChecksumReportSupport(const bool enabled)
|
||||
void Terminal::SetOptionalFeatures(winrt::Microsoft::Terminal::Core::ICoreSettings settings)
|
||||
{
|
||||
auto& engine = reinterpret_cast<OutputStateMachineEngine&>(_stateMachine->Engine());
|
||||
engine.Dispatch().SetVtChecksumReportSupport(enabled);
|
||||
auto features = til::enumset<ITermDispatch::OptionalFeature>{};
|
||||
features.set(ITermDispatch::OptionalFeature::ChecksumReport, settings.AllowVtChecksumReport());
|
||||
features.set(ITermDispatch::OptionalFeature::ClipboardWrite, settings.AllowVtClipboardWrite());
|
||||
engine.Dispatch().SetOptionalFeatures(features);
|
||||
}
|
||||
|
||||
bool Terminal::IsXtermBracketedPasteModeEnabled() const noexcept
|
||||
@@ -1571,10 +1574,10 @@ void Terminal::SerializeMainBuffer(const wchar_t* destination) const
|
||||
|
||||
void Terminal::ColorSelection(const TextAttribute& attr, winrt::Microsoft::Terminal::Core::MatchMode matchMode)
|
||||
{
|
||||
const auto colorSelection = [this](const til::point coordStart, const til::point coordEnd, const TextAttribute& attr) {
|
||||
const auto colorSelection = [this](const til::point coordStartInclusive, const til::point coordEndExclusive, const TextAttribute& attr) {
|
||||
auto& textBuffer = _activeBuffer();
|
||||
const auto spanLength = textBuffer.SpanLength(coordStart, coordEnd);
|
||||
textBuffer.Write(OutputCellIterator(attr, spanLength), coordStart);
|
||||
const auto spanLength = textBuffer.GetSize().CompareInBounds(coordEndExclusive, coordStartInclusive, true);
|
||||
textBuffer.Write(OutputCellIterator(attr, spanLength), coordStartInclusive);
|
||||
};
|
||||
|
||||
for (const auto [start, end] : _GetSelectionSpans())
|
||||
|
||||
@@ -94,7 +94,7 @@ public:
|
||||
void UpdateAppearance(const winrt::Microsoft::Terminal::Core::ICoreAppearance& appearance);
|
||||
void SetFontInfo(const FontInfo& fontInfo);
|
||||
void SetCursorStyle(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::CursorStyle cursorStyle);
|
||||
void SetVtChecksumReportSupport(const bool enabled);
|
||||
void SetOptionalFeatures(winrt::Microsoft::Terminal::Core::ICoreSettings settings);
|
||||
bool IsXtermBracketedPasteModeEnabled() const noexcept;
|
||||
std::wstring_view GetWorkingDirectory() noexcept;
|
||||
|
||||
|
||||
@@ -294,9 +294,21 @@ std::pair<til::point, til::point> Terminal::_ExpandSelectionAnchors(std::pair<ti
|
||||
|
||||
const auto& buffer = _activeBuffer();
|
||||
const auto bufferSize = buffer.GetSize();
|
||||
const auto height = buffer.GetSize().Height();
|
||||
|
||||
switch (_multiClickSelectionMode)
|
||||
{
|
||||
case SelectionExpansion::Line:
|
||||
// climb up to the first row that is wrapped
|
||||
while (start.y > 0 && buffer.GetRowByOffset(start.y - 1).WasWrapForced())
|
||||
{
|
||||
--start.y;
|
||||
}
|
||||
// climb down to the last row that is wrapped
|
||||
while (end.y + 1 < height && buffer.GetRowByOffset(end.y).WasWrapForced())
|
||||
{
|
||||
++end.y;
|
||||
}
|
||||
start = { bufferSize.Left(), start.y };
|
||||
end = { bufferSize.RightExclusive(), end.y };
|
||||
break;
|
||||
@@ -437,7 +449,8 @@ void Terminal::SelectHyperlink(const SearchDirection dir)
|
||||
}
|
||||
|
||||
// 0. Useful tools/vars
|
||||
const auto bufferSize = _activeBuffer().GetSize();
|
||||
const auto& buffer = _activeBuffer();
|
||||
const auto bufferSize = buffer.GetSize();
|
||||
const auto viewportHeight = _GetMutableViewport().Height();
|
||||
|
||||
// The patterns are stored relative to the "search area". Initially, this search area will be the viewport,
|
||||
@@ -504,8 +517,18 @@ void Terminal::SelectHyperlink(const SearchDirection dir)
|
||||
};
|
||||
|
||||
// 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;
|
||||
til::point searchStart;
|
||||
til::point searchEnd;
|
||||
if (dir == SearchDirection::Forward)
|
||||
{
|
||||
searchStart = _selection->start;
|
||||
searchEnd = til::point{ bufferSize.RightInclusive(), _VisibleEndIndex() };
|
||||
}
|
||||
else
|
||||
{
|
||||
searchStart = til::point{ bufferSize.Left(), _VisibleStartIndex() };
|
||||
searchEnd = _selection->start;
|
||||
}
|
||||
|
||||
// 1.A) Try searching the current viewport (no scrolling required)
|
||||
auto resultList = _patternIntervalTree.findContained(convertToSearchArea(searchStart), convertToSearchArea(searchEnd));
|
||||
@@ -547,27 +570,81 @@ void Terminal::SelectHyperlink(const SearchDirection dir)
|
||||
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
|
||||
// 2. We found a hyperlink from the pattern tree. Look for embedded hyperlinks too!
|
||||
// Use the result (if one was found) to narrow down the search.
|
||||
if (dir == SearchDirection::Forward)
|
||||
{
|
||||
auto selection{ _selection.write() };
|
||||
wil::hide_name _selection;
|
||||
selection->start = result->first;
|
||||
selection->pivot = result->first;
|
||||
selection->end = result->second;
|
||||
_selectionIsTargetingUrl = true;
|
||||
_selectionEndpoint = SelectionEndpoint::End;
|
||||
searchStart = _selection->start;
|
||||
searchEnd = (result ? result->first : buffer.GetLastNonSpaceCharacter());
|
||||
}
|
||||
else
|
||||
{
|
||||
searchStart = (result ? result->second : bufferSize.Origin());
|
||||
searchEnd = _selection->start;
|
||||
}
|
||||
|
||||
// 3. Scroll to the selected area (if necessary)
|
||||
_ScrollToPoint(_selection->end);
|
||||
// Careful! Selection can point to RightExclusive(), which doesn't contain data!
|
||||
// Clamp to be safe.
|
||||
auto initialPos = dir == SearchDirection::Forward ? searchStart : searchEnd;
|
||||
bufferSize.Clamp(initialPos);
|
||||
auto iter = buffer.GetCellDataAt(initialPos);
|
||||
while (dir == SearchDirection::Forward ? iter.Pos() < searchEnd : iter.Pos() > searchStart)
|
||||
{
|
||||
// Don't let us select the same hyperlink again
|
||||
if (iter.Pos() < _selection->start || iter.Pos() > _selection->end)
|
||||
{
|
||||
if (auto attr = iter->TextAttr(); attr.IsHyperlink())
|
||||
{
|
||||
// Found an embedded hyperlink!
|
||||
const auto hyperlinkId = attr.GetHyperlinkId();
|
||||
|
||||
// Expand the start to include the entire hyperlink
|
||||
TextBufferCellIterator hyperlinkStartIter{ buffer, iter.Pos() };
|
||||
while (hyperlinkStartIter.Pos() > searchStart && attr.IsHyperlink() && attr.GetHyperlinkId() == hyperlinkId)
|
||||
{
|
||||
--hyperlinkStartIter;
|
||||
attr = hyperlinkStartIter->TextAttr();
|
||||
}
|
||||
if (hyperlinkStartIter.Pos() != bufferSize.Origin())
|
||||
{
|
||||
// undo a move to be inclusive
|
||||
++hyperlinkStartIter;
|
||||
}
|
||||
|
||||
// Expand the end to include the entire hyperlink
|
||||
// No need to undo a move! We'll decrement in the next step anyways.
|
||||
TextBufferCellIterator hyperlinkEndIter{ buffer, iter.Pos() };
|
||||
attr = hyperlinkEndIter->TextAttr();
|
||||
while (hyperlinkEndIter.Pos() < searchEnd && attr.IsHyperlink() && attr.GetHyperlinkId() == hyperlinkId)
|
||||
{
|
||||
++hyperlinkEndIter;
|
||||
attr = hyperlinkEndIter->TextAttr();
|
||||
}
|
||||
|
||||
result = { hyperlinkStartIter.Pos(), hyperlinkEndIter.Pos() };
|
||||
break;
|
||||
}
|
||||
}
|
||||
iter += dir == SearchDirection::Forward ? 1 : -1;
|
||||
}
|
||||
|
||||
// 3. Select the hyperlink, if one exists
|
||||
if (!result.has_value())
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto selection{ _selection.write() };
|
||||
wil::hide_name _selection;
|
||||
selection->start = result->first;
|
||||
selection->pivot = result->first;
|
||||
selection->end = result->second;
|
||||
_selectionIsTargetingUrl = true;
|
||||
_selectionEndpoint = SelectionEndpoint::End;
|
||||
|
||||
// 4. Scroll to the selected area (if necessary)
|
||||
_ScrollToPoint(selection->end);
|
||||
}
|
||||
|
||||
Terminal::UpdateSelectionParams Terminal::ConvertKeyEventToUpdateSelectionParams(const ControlKeyStates mods, const WORD vkey) const noexcept
|
||||
|
||||
@@ -219,7 +219,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
// into the path TextBox, we properly update the checkbox and stored
|
||||
// _lastBgImagePath. Without this, then we'll permanently hide the text
|
||||
// box, prevent it from ever being changed again.
|
||||
_NotifyChanges(L"UseDesktopBGImage", L"BackgroundImageSettingsVisible");
|
||||
_NotifyChanges(L"UseDesktopBGImage", L"BackgroundImageSettingsVisible", L"CurrentBackgroundImagePath");
|
||||
}
|
||||
else if (viewModelProperty == L"BackgroundImageAlignment")
|
||||
{
|
||||
@@ -954,7 +954,21 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
return GetLibraryResourceString(alignmentResourceKey);
|
||||
}
|
||||
|
||||
bool AppearanceViewModel::UseDesktopBGImage()
|
||||
hstring AppearanceViewModel::CurrentBackgroundImagePath() const
|
||||
{
|
||||
const auto bgImagePath = BackgroundImagePath();
|
||||
if (bgImagePath.empty())
|
||||
{
|
||||
return RS_(L"Appearance_BackgroundImageNone");
|
||||
}
|
||||
else if (bgImagePath == L"desktopWallpaper")
|
||||
{
|
||||
return RS_(L"Profile_UseDesktopImage/Content");
|
||||
}
|
||||
return bgImagePath;
|
||||
}
|
||||
|
||||
bool AppearanceViewModel::UseDesktopBGImage() const
|
||||
{
|
||||
return BackgroundImagePath() == L"desktopWallpaper";
|
||||
}
|
||||
@@ -983,7 +997,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
}
|
||||
}
|
||||
|
||||
bool AppearanceViewModel::BackgroundImageSettingsVisible()
|
||||
bool AppearanceViewModel::BackgroundImageSettingsVisible() const
|
||||
{
|
||||
return !BackgroundImagePath().empty();
|
||||
}
|
||||
|
||||
@@ -120,9 +120,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
void UpdateFontSetting(const FontKeyValuePair* kv);
|
||||
|
||||
// background image
|
||||
bool UseDesktopBGImage();
|
||||
hstring CurrentBackgroundImagePath() const;
|
||||
bool UseDesktopBGImage() const;
|
||||
void UseDesktopBGImage(const bool useDesktop);
|
||||
bool BackgroundImageSettingsVisible();
|
||||
bool BackgroundImageSettingsVisible() const;
|
||||
void SetBackgroundImageOpacityFromPercentageValue(double percentageValue);
|
||||
void SetBackgroundImagePath(winrt::hstring path);
|
||||
hstring BackgroundImageAlignmentCurrentValue() const;
|
||||
|
||||
@@ -38,6 +38,7 @@ namespace Microsoft.Terminal.Settings.Editor
|
||||
void SetBackgroundImageOpacityFromPercentageValue(Double percentageValue);
|
||||
void SetBackgroundImagePath(String path);
|
||||
|
||||
String CurrentBackgroundImagePath { get; };
|
||||
Boolean UseDesktopBGImage;
|
||||
Boolean BackgroundImageSettingsVisible { get; };
|
||||
String BackgroundImageAlignmentCurrentValue { get; };
|
||||
|
||||
@@ -533,13 +533,13 @@
|
||||
<local:SettingContainer x:Name="BackgroundImageContainer"
|
||||
x:Uid="Profile_BackgroundImage"
|
||||
ClearSettingValue="{x:Bind Appearance.ClearBackgroundImagePath}"
|
||||
CurrentValue="{x:Bind Appearance.BackgroundImagePath, Mode=OneWay}"
|
||||
CurrentValue="{x:Bind Appearance.CurrentBackgroundImagePath, Mode=OneWay}"
|
||||
HasSettingValue="{x:Bind Appearance.HasBackgroundImagePath, Mode=OneWay}"
|
||||
SettingOverrideSource="{x:Bind Appearance.BackgroundImagePathOverrideSource, Mode=OneWay}"
|
||||
Style="{StaticResource ExpanderSettingContainerStyle}">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<TextBox x:Uid="Profile_BackgroundImageBox"
|
||||
IsEnabled="{x:Bind mtu:Converters.StringsAreNotEqual('desktopWallpaper', Appearance.BackgroundImagePath), Mode=OneWay}"
|
||||
IsEnabled="{x:Bind mtu:Converters.InvertBoolean(Appearance.UseDesktopBGImage), Mode=OneWay}"
|
||||
IsSpellCheckEnabled="False"
|
||||
Style="{StaticResource TextBoxSettingStyle}"
|
||||
Text="{x:Bind mtu:Converters.StringOrEmptyIfPlaceholder('desktopWallpaper', Appearance.BackgroundImagePath), Mode=TwoWay, BindBack=Appearance.SetBackgroundImagePath}" />
|
||||
@@ -547,7 +547,7 @@
|
||||
<Button x:Uid="Profile_BackgroundImageBrowse"
|
||||
Margin="0,10,10,0"
|
||||
Click="BackgroundImage_Click"
|
||||
IsEnabled="{x:Bind mtu:Converters.StringsAreNotEqual('desktopWallpaper', Appearance.BackgroundImagePath), Mode=OneWay}"
|
||||
IsEnabled="{x:Bind mtu:Converters.InvertBoolean(Appearance.UseDesktopBGImage), Mode=OneWay}"
|
||||
Style="{StaticResource BrowseButtonStyle}" />
|
||||
<CheckBox x:Name="UseDesktopImageCheckBox"
|
||||
x:Uid="Profile_UseDesktopImage"
|
||||
|
||||
@@ -467,7 +467,7 @@
|
||||
<FontIcon Margin="0,0,-1,-1"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom"
|
||||
FontFamily="Segoe Fluent Icons"
|
||||
FontFamily="Segoe Fluent Icons, Segoe MDL2 Assets"
|
||||
FontSize="12"
|
||||
Foreground="{TemplateBinding BorderBrush}"
|
||||
Glyph=""
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "Launch.h"
|
||||
#include "Launch.g.cpp"
|
||||
#include "EnumEntry.h"
|
||||
#include "LaunchViewModel.h"
|
||||
|
||||
#include <LibraryResources.h>
|
||||
|
||||
@@ -40,5 +41,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
void Launch::OnNavigatedTo(const NavigationEventArgs& e)
|
||||
{
|
||||
_ViewModel = e.Parameter().as<Editor::LaunchViewModel>();
|
||||
auto innerViewModel{ winrt::get_self<Editor::implementation::LaunchViewModel>(_ViewModel) };
|
||||
/* coroutine dispatch */ innerViewModel->PrepareStartOnUserLoginSettings();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,8 +140,7 @@
|
||||
</local:SettingContainer>
|
||||
|
||||
<!-- Language -->
|
||||
<local:SettingContainer x:Uid="Globals_Language"
|
||||
Visibility="{x:Bind ViewModel.LanguageSelectorAvailable}">
|
||||
<local:SettingContainer x:Uid="Globals_Language">
|
||||
<ComboBox ItemsSource="{x:Bind ViewModel.LanguageList}"
|
||||
SelectedItem="{x:Bind ViewModel.CurrentLanguage, Mode=TwoWay}"
|
||||
Style="{StaticResource ComboBoxSettingStyle}">
|
||||
@@ -163,8 +162,11 @@
|
||||
</local:SettingContainer>
|
||||
|
||||
<!-- Start on User Login -->
|
||||
<local:SettingContainer x:Uid="Globals_StartOnUserLogin">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.StartOnUserLogin, Mode=TwoWay}"
|
||||
<local:SettingContainer x:Uid="Globals_StartOnUserLogin"
|
||||
HelpText="{x:Bind ViewModel.StartOnUserLoginStatefulHelpText, Mode=OneWay}"
|
||||
Visibility="{x:Bind ViewModel.StartOnUserLoginAvailable, Mode=OneTime}">
|
||||
<ToggleSwitch IsEnabled="{x:Bind ViewModel.StartOnUserLoginConfigurable, Mode=OneWay}"
|
||||
IsOn="{x:Bind ViewModel.StartOnUserLogin, Mode=TwoWay}"
|
||||
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
|
||||
</local:SettingContainer>
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@ using namespace winrt::Windows::Foundation;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
using namespace winrt::Windows::UI::Xaml::Data;
|
||||
|
||||
static constexpr std::wstring_view StartupTaskName = L"StartTerminalOnLoginTask";
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
// For ComboBox an empty SelectedItem string denotes no selection.
|
||||
@@ -80,16 +82,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
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 LaunchViewModel::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.
|
||||
@@ -100,12 +92,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
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].
|
||||
@@ -177,19 +163,24 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
return _currentLanguage;
|
||||
}
|
||||
|
||||
if (!LanguageSelectorAvailable())
|
||||
winrt::hstring currentLanguage;
|
||||
if (IsPackaged())
|
||||
{
|
||||
_currentLanguage = {};
|
||||
return _currentLanguage;
|
||||
// NOTE: PrimaryLanguageOverride throws if this instance is unpackaged.
|
||||
currentLanguage = winrt::Windows::Globalization::ApplicationLanguages::PrimaryLanguageOverride();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_Settings.GlobalSettings().HasLanguage())
|
||||
{
|
||||
currentLanguage = _Settings.GlobalSettings().Language();
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
@@ -365,4 +356,86 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
return _Settings.DefaultTerminals();
|
||||
}
|
||||
|
||||
bool LaunchViewModel::StartOnUserLoginAvailable()
|
||||
{
|
||||
return IsPackaged();
|
||||
}
|
||||
|
||||
safe_void_coroutine LaunchViewModel::PrepareStartOnUserLoginSettings()
|
||||
{
|
||||
if (!StartOnUserLoginAvailable())
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
|
||||
auto strongThis{ get_strong() };
|
||||
auto task{ co_await winrt::Windows::ApplicationModel::StartupTask::GetAsync(StartupTaskName) };
|
||||
_startOnUserLoginTask = std::move(task);
|
||||
_NotifyChanges(L"StartOnUserLoginConfigurable", L"StartOnUserLoginStatefulHelpText", L"StartOnUserLogin");
|
||||
}
|
||||
|
||||
bool LaunchViewModel::StartOnUserLoginConfigurable()
|
||||
{
|
||||
if (!_startOnUserLoginTask)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
namespace WAM = winrt::Windows::ApplicationModel;
|
||||
const auto state{ _startOnUserLoginTask.State() };
|
||||
// Terminal cannot change the state of the login task if it is any of the "ByUser" or "ByPolicy" states.
|
||||
return state == WAM::StartupTaskState::Disabled || state == WAM::StartupTaskState::Enabled;
|
||||
}
|
||||
|
||||
winrt::hstring LaunchViewModel::StartOnUserLoginStatefulHelpText()
|
||||
{
|
||||
if (_startOnUserLoginTask)
|
||||
{
|
||||
namespace WAM = winrt::Windows::ApplicationModel;
|
||||
switch (_startOnUserLoginTask.State())
|
||||
{
|
||||
case WAM::StartupTaskState::EnabledByPolicy:
|
||||
case WAM::StartupTaskState::DisabledByPolicy:
|
||||
return winrt::hstring{ L"\uE72E " } /*lock icon*/ + RS_(L"Globals_StartOnUserLogin_UnavailableByPolicy");
|
||||
case WAM::StartupTaskState::DisabledByUser:
|
||||
return RS_(L"Globals_StartOnUserLogin_DisabledByUser");
|
||||
case WAM::StartupTaskState::Enabled:
|
||||
case WAM::StartupTaskState::Disabled:
|
||||
default:
|
||||
break; // fall through to the common case (no task, not configured, etc.)
|
||||
}
|
||||
}
|
||||
return RS_(L"Globals_StartOnUserLogin/HelpText");
|
||||
}
|
||||
|
||||
bool LaunchViewModel::StartOnUserLogin()
|
||||
{
|
||||
if (!_startOnUserLoginTask)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
namespace WAM = winrt::Windows::ApplicationModel;
|
||||
const auto state{ _startOnUserLoginTask.State() };
|
||||
return state == WAM::StartupTaskState::Enabled || state == WAM::StartupTaskState::EnabledByPolicy;
|
||||
}
|
||||
|
||||
safe_void_coroutine LaunchViewModel::StartOnUserLogin(bool enable)
|
||||
{
|
||||
if (!_startOnUserLoginTask)
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
|
||||
auto strongThis{ get_strong() };
|
||||
if (enable)
|
||||
{
|
||||
co_await _startOnUserLoginTask.RequestEnableAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
_startOnUserLoginTask.Disable();
|
||||
}
|
||||
// Any of these could have changed in response to an attempt to enable (e.g. it was disabled in task manager since our last check)
|
||||
_NotifyChanges(L"StartOnUserLoginConfigurable", L"StartOnUserLoginStatefulHelpText", L"StartOnUserLogin");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "LaunchViewModel.g.h"
|
||||
#include "ViewModelHelpers.h"
|
||||
#include "Utils.h"
|
||||
#include <cppwinrt_utils.h>
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
@@ -19,7 +20,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
// "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);
|
||||
@@ -51,10 +51,16 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
GETSET_BINDABLE_ENUM_SETTING(WindowingBehavior, Model::WindowingMode, _Settings.GlobalSettings().WindowingBehavior);
|
||||
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_Settings.GlobalSettings(), CenterOnLaunch);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_Settings.GlobalSettings(), StartOnUserLogin);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_Settings.GlobalSettings(), InitialRows);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_Settings.GlobalSettings(), InitialCols);
|
||||
|
||||
bool StartOnUserLoginAvailable();
|
||||
safe_void_coroutine PrepareStartOnUserLoginSettings();
|
||||
bool StartOnUserLoginConfigurable();
|
||||
winrt::hstring StartOnUserLoginStatefulHelpText();
|
||||
bool StartOnUserLogin();
|
||||
safe_void_coroutine StartOnUserLogin(bool enable);
|
||||
|
||||
private:
|
||||
Model::CascadiaSettings _Settings;
|
||||
winrt::Windows::Foundation::Collections::IObservableVector<winrt::hstring> _languageList;
|
||||
@@ -63,6 +69,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
|
||||
winrt::Windows::Foundation::Collections::IObservableVector<winrt::Microsoft::Terminal::Settings::Editor::EnumEntry> _LaunchModeList;
|
||||
winrt::Windows::Foundation::Collections::IMap<Model::LaunchMode, winrt::Microsoft::Terminal::Settings::Editor::EnumEntry> _LaunchModeMap;
|
||||
|
||||
winrt::Windows::ApplicationModel::StartupTask _startOnUserLoginTask{ nullptr };
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -9,10 +9,7 @@ namespace Microsoft.Terminal.Settings.Editor
|
||||
{
|
||||
runtimeclass LaunchViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
LaunchViewModel(Microsoft.Terminal.Settings.Model.CascadiaSettings settings);
|
||||
|
||||
static String LanguageDisplayConverter(String tag);
|
||||
Boolean LanguageSelectorAvailable { get; };
|
||||
Windows.Foundation.Collections.IObservableVector<String> LanguageList { get; };
|
||||
IInspectable CurrentLanguage;
|
||||
|
||||
@@ -41,8 +38,12 @@ namespace Microsoft.Terminal.Settings.Editor
|
||||
IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> WindowingBehaviorList { get; };
|
||||
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, CenterOnLaunch);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, StartOnUserLogin);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Int32, InitialRows);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Int32, InitialCols);
|
||||
|
||||
Boolean StartOnUserLogin { get; set; };
|
||||
Boolean StartOnUserLoginAvailable { get; };
|
||||
Boolean StartOnUserLoginConfigurable { get; };
|
||||
String StartOnUserLoginStatefulHelpText { get; };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ using namespace winrt::Windows::System;
|
||||
using namespace winrt::Windows::UI::Xaml::Controls;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
|
||||
static const std::wstring_view openJsonTag{ L"OpenJson_Nav" };
|
||||
static const std::wstring_view launchTag{ L"Launch_Nav" };
|
||||
static const std::wstring_view interactionTag{ L"Interaction_Nav" };
|
||||
static const std::wstring_view renderingTag{ L"Rendering_Nav" };
|
||||
@@ -324,6 +325,17 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
|
||||
if (const auto navString = clickedItemContainer.Tag().try_as<hstring>())
|
||||
{
|
||||
if (*navString == openJsonTag)
|
||||
{
|
||||
const auto window = CoreWindow::GetForCurrentThread();
|
||||
const auto rAltState = window.GetKeyState(VirtualKey::RightMenu);
|
||||
const auto lAltState = window.GetKeyState(VirtualKey::LeftMenu);
|
||||
const auto altPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) ||
|
||||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
|
||||
const auto target = altPressed ? SettingsTarget::DefaultsFile : SettingsTarget::SettingsFile;
|
||||
OpenJson.raise(nullptr, target);
|
||||
return;
|
||||
}
|
||||
_Navigate(*navString, BreadcrumbSubPage::None);
|
||||
}
|
||||
else if (const auto profile = clickedItemContainer.Tag().try_as<Editor::ProfileViewModel>())
|
||||
@@ -575,31 +587,13 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void MainPage::OpenJsonTapped(const IInspectable& /*sender*/, const Windows::UI::Xaml::Input::TappedRoutedEventArgs& /*args*/)
|
||||
{
|
||||
const auto window = CoreWindow::GetForCurrentThread();
|
||||
const auto rAltState = window.GetKeyState(VirtualKey::RightMenu);
|
||||
const auto lAltState = window.GetKeyState(VirtualKey::LeftMenu);
|
||||
const auto altPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) ||
|
||||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
|
||||
|
||||
const auto target = altPressed ? SettingsTarget::DefaultsFile : SettingsTarget::SettingsFile;
|
||||
OpenJson.raise(nullptr, target);
|
||||
}
|
||||
|
||||
void MainPage::OpenJsonKeyDown(const IInspectable& /*sender*/, const Windows::UI::Xaml::Input::KeyRoutedEventArgs& args)
|
||||
{
|
||||
if (args.Key() == VirtualKey::Enter || args.Key() == VirtualKey::Space)
|
||||
{
|
||||
const auto target = args.KeyStatus().IsMenuKeyDown ? SettingsTarget::DefaultsFile : SettingsTarget::SettingsFile;
|
||||
OpenJson.raise(nullptr, target);
|
||||
}
|
||||
}
|
||||
|
||||
void MainPage::SaveButton_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*args*/)
|
||||
{
|
||||
_settingsClone.LogSettingChanges(false);
|
||||
_settingsClone.WriteSettingsToDisk();
|
||||
if (!_settingsClone.WriteSettingsToDisk())
|
||||
{
|
||||
ShowLoadWarningsDialog.raise(*this, _settingsClone.Warnings());
|
||||
}
|
||||
}
|
||||
|
||||
void MainPage::ResetButton_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*args*/)
|
||||
|
||||
@@ -30,8 +30,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
|
||||
void UpdateSettings(const Model::CascadiaSettings& settings);
|
||||
|
||||
void OpenJsonKeyDown(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::KeyRoutedEventArgs& args);
|
||||
void OpenJsonTapped(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::TappedRoutedEventArgs& args);
|
||||
void SettingsNav_Loaded(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);
|
||||
void SettingsNav_ItemInvoked(const Microsoft::UI::Xaml::Controls::NavigationView& sender, const Microsoft::UI::Xaml::Controls::NavigationViewItemInvokedEventArgs& args);
|
||||
void SaveButton_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);
|
||||
@@ -47,6 +45,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
Windows::Foundation::Collections::IObservableVector<IInspectable> Breadcrumbs() noexcept;
|
||||
|
||||
til::typed_event<Windows::Foundation::IInspectable, Model::SettingsTarget> OpenJson;
|
||||
til::typed_event<Windows::Foundation::IInspectable, Windows::Foundation::Collections::IVectorView<Model::SettingsLoadWarnings>> ShowLoadWarningsDialog;
|
||||
|
||||
private:
|
||||
Windows::Foundation::Collections::IObservableVector<IInspectable> _breadcrumbs;
|
||||
|
||||
@@ -36,6 +36,7 @@ namespace Microsoft.Terminal.Settings.Editor
|
||||
|
||||
void UpdateSettings(Microsoft.Terminal.Settings.Model.CascadiaSettings settings);
|
||||
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Settings.Model.SettingsTarget> OpenJson;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Windows.Foundation.Collections.IVectorView<Microsoft.Terminal.Settings.Model.SettingsLoadWarnings> > ShowLoadWarningsDialog;
|
||||
|
||||
// Due to the aforementioned bug, we can't use IInitializeWithWindow _here_ either.
|
||||
// Let's just smuggle the HWND in as a UInt64 :|
|
||||
|
||||
@@ -171,8 +171,8 @@
|
||||
<!-- The OpenJson item needs both Tapped and KeyDown handler -->
|
||||
<muxc:NavigationViewItem x:Name="OpenJsonNavItem"
|
||||
x:Uid="Nav_OpenJSON"
|
||||
KeyDown="OpenJsonKeyDown"
|
||||
Tapped="OpenJsonTapped">
|
||||
SelectsOnInvoked="False"
|
||||
Tag="OpenJson_Nav">
|
||||
<muxc:NavigationViewItem.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
</muxc:NavigationViewItem.Icon>
|
||||
|
||||
@@ -363,7 +363,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
|
||||
const auto& entryVM = make<ProfileEntryViewModel>(profileEntry);
|
||||
CurrentView().Append(entryVM);
|
||||
_PrintAll();
|
||||
return entryVM;
|
||||
}
|
||||
return nullptr;
|
||||
@@ -374,8 +373,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
Model::SeparatorEntry separatorEntry;
|
||||
const auto& entryVM = make<SeparatorEntryViewModel>(separatorEntry);
|
||||
CurrentView().Append(entryVM);
|
||||
|
||||
_PrintAll();
|
||||
return entryVM;
|
||||
}
|
||||
|
||||
@@ -390,8 +387,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
// Reset state after adding the entry
|
||||
AddFolderName({});
|
||||
_folderTreeCache = nullptr;
|
||||
|
||||
_PrintAll();
|
||||
return entryVM;
|
||||
}
|
||||
|
||||
@@ -410,7 +405,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
ProfileMatcherSource({});
|
||||
ProfileMatcherCommandline({});
|
||||
|
||||
_PrintAll();
|
||||
return entryVM;
|
||||
}
|
||||
|
||||
@@ -422,7 +416,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
|
||||
_NotifyChanges(L"IsRemainingProfilesEntryMissing");
|
||||
|
||||
_PrintAll();
|
||||
return entryVM;
|
||||
}
|
||||
|
||||
@@ -496,134 +489,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
return _folderEntry.Icon();
|
||||
}
|
||||
|
||||
void NewTabMenuViewModel::_PrintAll()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
OutputDebugString(L"---Model:---\n");
|
||||
_PrintModel(_Settings.GlobalSettings().NewTabMenu());
|
||||
OutputDebugString(L"\n");
|
||||
OutputDebugString(L"---VM:---\n");
|
||||
_PrintVM(_rootEntries);
|
||||
OutputDebugString(L"\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
void NewTabMenuViewModel::_PrintModel(Windows::Foundation::Collections::IVector<Model::NewTabMenuEntry> list, std::wstring prefix)
|
||||
{
|
||||
if (!list)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto&& e : list)
|
||||
{
|
||||
_PrintModel(e, prefix);
|
||||
}
|
||||
}
|
||||
|
||||
void NewTabMenuViewModel::_PrintModel(const Model::NewTabMenuEntry& e, std::wstring prefix)
|
||||
{
|
||||
switch (e.Type())
|
||||
{
|
||||
case NewTabMenuEntryType::Profile:
|
||||
{
|
||||
const auto& pe = e.as<Model::ProfileEntry>();
|
||||
OutputDebugString(fmt::format(L"{}Profile: {}\n", prefix, pe.Profile().Name()).c_str());
|
||||
break;
|
||||
}
|
||||
case NewTabMenuEntryType::Action:
|
||||
{
|
||||
const auto& actionEntry = e.as<Model::ActionEntry>();
|
||||
OutputDebugString(fmt::format(L"{}Action: {}\n", prefix, actionEntry.ActionId()).c_str());
|
||||
break;
|
||||
}
|
||||
case NewTabMenuEntryType::Separator:
|
||||
{
|
||||
OutputDebugString(fmt::format(L"{}Separator\n", prefix).c_str());
|
||||
break;
|
||||
}
|
||||
case NewTabMenuEntryType::Folder:
|
||||
{
|
||||
const auto& fe = e.as<Model::FolderEntry>();
|
||||
OutputDebugString(fmt::format(L"{}Folder: {}\n", prefix, fe.Name()).c_str());
|
||||
_PrintModel(fe.RawEntries(), prefix + L" ");
|
||||
break;
|
||||
}
|
||||
case NewTabMenuEntryType::MatchProfiles:
|
||||
{
|
||||
const auto& matchProfilesEntry = e.as<Model::MatchProfilesEntry>();
|
||||
OutputDebugString(fmt::format(L"{}MatchProfiles: {}\n", prefix, matchProfilesEntry.Name()).c_str());
|
||||
break;
|
||||
}
|
||||
case NewTabMenuEntryType::RemainingProfiles:
|
||||
{
|
||||
OutputDebugString(fmt::format(L"{}RemainingProfiles\n", prefix).c_str());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void NewTabMenuViewModel::_PrintVM(Windows::Foundation::Collections::IVector<Editor::NewTabMenuEntryViewModel> list, std::wstring prefix)
|
||||
{
|
||||
if (!list)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto&& e : list)
|
||||
{
|
||||
_PrintVM(e, prefix);
|
||||
}
|
||||
}
|
||||
|
||||
void NewTabMenuViewModel::_PrintVM(const Editor::NewTabMenuEntryViewModel& e, std::wstring prefix)
|
||||
{
|
||||
switch (e.Type())
|
||||
{
|
||||
case NewTabMenuEntryType::Profile:
|
||||
{
|
||||
const auto& pe = e.as<Editor::ProfileEntryViewModel>();
|
||||
OutputDebugString(fmt::format(L"{}Profile: {}\n", prefix, pe.ProfileEntry().Profile().Name()).c_str());
|
||||
break;
|
||||
}
|
||||
case NewTabMenuEntryType::Action:
|
||||
{
|
||||
const auto& actionEntry = e.as<Editor::ActionEntryViewModel>();
|
||||
OutputDebugString(fmt::format(L"{}Action: {}\n", prefix, actionEntry.ActionEntry().ActionId()).c_str());
|
||||
break;
|
||||
}
|
||||
case NewTabMenuEntryType::Separator:
|
||||
{
|
||||
OutputDebugString(fmt::format(L"{}Separator\n", prefix).c_str());
|
||||
break;
|
||||
}
|
||||
case NewTabMenuEntryType::Folder:
|
||||
{
|
||||
const auto& fe = e.as<Editor::FolderEntryViewModel>();
|
||||
OutputDebugString(fmt::format(L"{}Folder: {}\n", prefix, fe.Name()).c_str());
|
||||
_PrintVM(fe.Entries(), prefix + L" ");
|
||||
break;
|
||||
}
|
||||
case NewTabMenuEntryType::MatchProfiles:
|
||||
{
|
||||
const auto& matchProfilesEntry = e.as<Editor::MatchProfilesEntryViewModel>();
|
||||
OutputDebugString(fmt::format(L"{}MatchProfiles: {}\n", prefix, matchProfilesEntry.DisplayText()).c_str());
|
||||
break;
|
||||
}
|
||||
case NewTabMenuEntryType::RemainingProfiles:
|
||||
{
|
||||
OutputDebugString(fmt::format(L"{}RemainingProfiles\n", prefix).c_str());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
NewTabMenuEntryViewModel::NewTabMenuEntryViewModel(const NewTabMenuEntryType type) noexcept :
|
||||
_Type{ type }
|
||||
{
|
||||
|
||||
@@ -68,14 +68,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
|
||||
static bool _IsRemainingProfilesEntryMissing(const Windows::Foundation::Collections::IVector<Editor::NewTabMenuEntryViewModel>& entries);
|
||||
void _FolderPropertyChanged(const IInspectable& sender, const Windows::UI::Xaml::Data::PropertyChangedEventArgs& args);
|
||||
|
||||
void _PrintAll();
|
||||
#ifdef _DEBUG
|
||||
void _PrintModel(Windows::Foundation::Collections::IVector<Model::NewTabMenuEntry> list, std::wstring prefix = L"");
|
||||
void _PrintModel(const Model::NewTabMenuEntry& e, std::wstring prefix = L"");
|
||||
void _PrintVM(Windows::Foundation::Collections::IVector<Editor::NewTabMenuEntryViewModel> list, std::wstring prefix = L"");
|
||||
void _PrintVM(const Editor::NewTabMenuEntryViewModel& vm, std::wstring prefix = L"");
|
||||
#endif
|
||||
};
|
||||
|
||||
struct FolderTreeViewEntry : FolderTreeViewEntryT<FolderTreeViewEntry>
|
||||
|
||||
@@ -94,12 +94,9 @@
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid ColumnSpacing="5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<StackPanel x:Name="ContentStackPanel"
|
||||
Orientation="Horizontal"
|
||||
Spacing="5">
|
||||
<ContentDialog x:Name="ColorPickerDialog"
|
||||
x:Uid="NullableColorPicker_ColorPickerContentDialog"
|
||||
DefaultButton="Primary"
|
||||
@@ -119,37 +116,61 @@
|
||||
Orientation="Horizontal" />
|
||||
</ContentDialog>
|
||||
|
||||
<ContentPresenter Grid.Column="0"
|
||||
Content="{x:Bind ColorSchemeVM, Mode=OneWay}"
|
||||
<ContentPresenter Content="{x:Bind ColorSchemeVM, Mode=OneWay}"
|
||||
ContentTemplate="{StaticResource ColorSchemeTemplate}" />
|
||||
|
||||
<StackPanel Grid.Column="1"
|
||||
Spacing="5">
|
||||
<ToggleButton AutomationProperties.Name="{x:Bind NullColorButtonLabel}"
|
||||
Click="NullColorButton_Clicked"
|
||||
IsChecked="{x:Bind IsNull(CurrentColor), Mode=OneWay}">
|
||||
<Grid ColumnSpacing="5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Spacing="5">
|
||||
<ToggleButton AutomationProperties.Name="{x:Bind NullColorButtonLabel}"
|
||||
Click="NullColorButton_Clicked"
|
||||
IsChecked="{x:Bind IsNull(CurrentColor), Mode=OneWay}">
|
||||
<Grid ColumnSpacing="5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Border Grid.Column="0"
|
||||
Width="20"
|
||||
Height="20"
|
||||
Background="{x:Bind mtu:Converters.ColorToBrush(NullColorPreview), Mode=OneWay}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="{ThemeResource ControlCornerRadius}" />
|
||||
<Border Grid.Column="0"
|
||||
Width="20"
|
||||
Height="20"
|
||||
Background="{x:Bind mtu:Converters.ColorToBrush(NullColorPreview), Mode=OneWay}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="{ThemeResource ControlCornerRadius}" />
|
||||
|
||||
<TextBlock Grid.Column="1"
|
||||
Text="{x:Bind NullColorButtonLabel}" />
|
||||
</Grid>
|
||||
</ToggleButton>
|
||||
<TextBlock Grid.Column="1"
|
||||
Text="{x:Bind NullColorButtonLabel}" />
|
||||
</Grid>
|
||||
</ToggleButton>
|
||||
|
||||
<Button x:Uid="NullableColorPicker_MoreColorsButton"
|
||||
HorizontalAlignment="Stretch"
|
||||
Click="MoreColors_Clicked" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Button x:Uid="NullableColorPicker_MoreColorsButton"
|
||||
HorizontalAlignment="Stretch"
|
||||
Click="MoreColors_Clicked" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup>
|
||||
<VisualState x:Name="Narrow">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="0" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ContentStackPanel.Orientation" Value="Vertical" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Wide">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="600" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ContentStackPanel.Orientation" Value="Horizontal" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</StackPanel>
|
||||
|
||||
</UserControl>
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
// notify listener that all starting directory related values might have changed
|
||||
// NOTE: this is similar to what is done with BackgroundImagePath above
|
||||
_NotifyChanges(L"UseParentProcessDirectory", L"UseCustomStartingDirectory");
|
||||
_NotifyChanges(L"UseParentProcessDirectory", L"CurrentStartingDirectoryPreview");
|
||||
}
|
||||
else if (viewModelProperty == L"AntialiasingMode")
|
||||
{
|
||||
@@ -83,7 +83,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
}
|
||||
else if (viewModelProperty == L"BellStyle")
|
||||
{
|
||||
_NotifyChanges(L"IsBellStyleFlagSet");
|
||||
_NotifyChanges(L"IsBellStyleFlagSet", L"BellStylePreview");
|
||||
}
|
||||
else if (viewModelProperty == L"ScrollState")
|
||||
{
|
||||
@@ -139,6 +139,14 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
_parsedPadding = StringToXamlThickness(_profile.Padding());
|
||||
_NotifyChanges(L"LeftPadding", L"TopPadding", L"RightPadding", L"BottomPadding");
|
||||
}
|
||||
else if (viewModelProperty == L"TabTitle")
|
||||
{
|
||||
_NotifyChanges(L"TabTitlePreview");
|
||||
}
|
||||
else if (viewModelProperty == L"AnswerbackMessage")
|
||||
{
|
||||
_NotifyChanges(L"AnswerbackMessagePreview");
|
||||
}
|
||||
});
|
||||
|
||||
// Do the same for the starting directory
|
||||
@@ -413,6 +421,15 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
return RS_(L"Profile_TabTitleNone");
|
||||
}
|
||||
|
||||
hstring ProfileViewModel::AnswerbackMessagePreview() const
|
||||
{
|
||||
if (const auto answerbackMessage{ AnswerbackMessage() }; !answerbackMessage.empty())
|
||||
{
|
||||
return answerbackMessage;
|
||||
}
|
||||
return RS_(L"Profile_AnswerbackMessageNone");
|
||||
}
|
||||
|
||||
Editor::AppearanceViewModel ProfileViewModel::DefaultAppearance()
|
||||
{
|
||||
return _defaultAppearanceViewModel;
|
||||
@@ -470,18 +487,18 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
return Feature_ScrollbarMarks::IsEnabled();
|
||||
}
|
||||
|
||||
bool ProfileViewModel::UseParentProcessDirectory()
|
||||
hstring ProfileViewModel::CurrentStartingDirectoryPreview() const
|
||||
{
|
||||
return StartingDirectory().empty();
|
||||
if (UseParentProcessDirectory())
|
||||
{
|
||||
return RS_(L"Profile_StartingDirectoryUseParentCheckbox/Content");
|
||||
}
|
||||
return StartingDirectory();
|
||||
}
|
||||
|
||||
// This function simply returns the opposite of UseParentProcessDirectory.
|
||||
// We bind the 'IsEnabled' parameters of the textbox and browse button
|
||||
// to this because it needs to be the reverse of UseParentProcessDirectory
|
||||
// but we don't want to create a whole new converter for inverting a boolean
|
||||
bool ProfileViewModel::UseCustomStartingDirectory()
|
||||
bool ProfileViewModel::UseParentProcessDirectory() const
|
||||
{
|
||||
return !UseParentProcessDirectory();
|
||||
return StartingDirectory().empty();
|
||||
}
|
||||
|
||||
void ProfileViewModel::UseParentProcessDirectory(const bool useParent)
|
||||
@@ -613,6 +630,49 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
return _currentIconType == _IconTypes.GetAt(3);
|
||||
}
|
||||
|
||||
hstring ProfileViewModel::BellStylePreview() const
|
||||
{
|
||||
const auto bellStyle = BellStyle();
|
||||
if (WI_AreAllFlagsSet(bellStyle, BellStyle::Audible | BellStyle::Window | BellStyle::Taskbar))
|
||||
{
|
||||
return RS_(L"Profile_BellStyleAll/Content");
|
||||
}
|
||||
else if (bellStyle == static_cast<Model::BellStyle>(0))
|
||||
{
|
||||
return RS_(L"Profile_BellStyleNone/Content");
|
||||
}
|
||||
|
||||
std::vector<hstring> resultList;
|
||||
resultList.reserve(3);
|
||||
if (WI_IsFlagSet(bellStyle, BellStyle::Audible))
|
||||
{
|
||||
resultList.emplace_back(RS_(L"Profile_BellStyleAudible/Content"));
|
||||
}
|
||||
if (WI_IsFlagSet(bellStyle, BellStyle::Window))
|
||||
{
|
||||
resultList.emplace_back(RS_(L"Profile_BellStyleWindow/Content"));
|
||||
}
|
||||
if (WI_IsFlagSet(bellStyle, BellStyle::Taskbar))
|
||||
{
|
||||
resultList.emplace_back(RS_(L"Profile_BellStyleTaskbar/Content"));
|
||||
}
|
||||
|
||||
// add in the commas
|
||||
hstring result{};
|
||||
for (auto&& entry : resultList)
|
||||
{
|
||||
if (result.empty())
|
||||
{
|
||||
result = entry;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = result + L", " + entry;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ProfileViewModel::IsBellStyleFlagSet(const uint32_t flag)
|
||||
{
|
||||
return (WI_EnumValue(BellStyle()) & flag) == flag;
|
||||
|
||||
@@ -55,6 +55,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
void SetupAppearances(Windows::Foundation::Collections::IObservableVector<Editor::ColorSchemeViewModel> schemesList);
|
||||
|
||||
// bell style bits
|
||||
hstring BellStylePreview() const;
|
||||
bool IsBellStyleFlagSet(const uint32_t flag);
|
||||
void SetBellStyleAudible(winrt::Windows::Foundation::IReference<bool> on);
|
||||
void SetBellStyleWindow(winrt::Windows::Foundation::IReference<bool> on);
|
||||
@@ -95,9 +96,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
bool UsingImageIcon() const;
|
||||
|
||||
// starting directory
|
||||
bool UseParentProcessDirectory();
|
||||
hstring CurrentStartingDirectoryPreview() const;
|
||||
bool UseParentProcessDirectory() const;
|
||||
void UseParentProcessDirectory(const bool useParent);
|
||||
bool UseCustomStartingDirectory();
|
||||
|
||||
// general profile knowledge
|
||||
winrt::guid OriginalProfileGuid() const noexcept;
|
||||
@@ -116,6 +117,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
|
||||
bool Orphaned() const;
|
||||
hstring TabTitlePreview() const;
|
||||
hstring AnswerbackMessagePreview() const;
|
||||
|
||||
til::typed_event<Editor::ProfileViewModel, Editor::DeleteProfileEventArgs> DeleteProfileRequested;
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ namespace Microsoft.Terminal.Settings.Editor
|
||||
|
||||
void SetAcrylicOpacityPercentageValue(Double value);
|
||||
|
||||
String BellStylePreview { get; };
|
||||
Boolean IsBellStyleFlagSet(UInt32 flag);
|
||||
void SetBellStyleAudible(Windows.Foundation.IReference<Boolean> on);
|
||||
void SetBellStyleWindow(Windows.Foundation.IReference<Boolean> on);
|
||||
@@ -87,12 +88,13 @@ namespace Microsoft.Terminal.Settings.Editor
|
||||
IInspectable CurrentPathTranslationStyle;
|
||||
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> PathTranslationStyleList { get; };
|
||||
|
||||
String CurrentStartingDirectoryPreview { get; };
|
||||
Boolean UseParentProcessDirectory;
|
||||
|
||||
Boolean CanDeleteProfile { get; };
|
||||
Boolean FocusDeleteButton;
|
||||
Boolean IsBaseLayer;
|
||||
ProfileSubPage CurrentPage;
|
||||
Boolean UseParentProcessDirectory;
|
||||
Boolean UseCustomStartingDirectory { get; };
|
||||
AppearanceViewModel DefaultAppearance { get; };
|
||||
Guid OriginalProfileGuid { get; };
|
||||
Boolean HasUnfocusedAppearance { get; };
|
||||
@@ -119,6 +121,7 @@ namespace Microsoft.Terminal.Settings.Editor
|
||||
Windows.Foundation.Collections.IVector<IInspectable> BuiltInIcons { get; };
|
||||
|
||||
String TabTitlePreview { get; };
|
||||
String AnswerbackMessagePreview { get; };
|
||||
|
||||
void CreateUnfocusedAppearance();
|
||||
void DeleteUnfocusedAppearance();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user