commit
dc865529b5
|
@ -1,10 +1,13 @@
|
|||
Apc
|
||||
apc
|
||||
calt
|
||||
ccmp
|
||||
Apc
|
||||
clickable
|
||||
clig
|
||||
copyable
|
||||
dalet
|
||||
Dcs
|
||||
dcs
|
||||
Dcs
|
||||
dialytika
|
||||
dje
|
||||
downside
|
||||
|
@ -12,8 +15,10 @@ downsides
|
|||
dze
|
||||
dzhe
|
||||
Enum'd
|
||||
Fitt
|
||||
formattings
|
||||
ftp
|
||||
fvar
|
||||
geeksforgeeks
|
||||
ghe
|
||||
gje
|
||||
|
@ -27,7 +32,10 @@ It'd
|
|||
kje
|
||||
liga
|
||||
lje
|
||||
locl
|
||||
lorem
|
||||
maxed
|
||||
mkmk
|
||||
mru
|
||||
nje
|
||||
ogonek
|
||||
|
@ -37,10 +45,12 @@ postmodern
|
|||
ptys
|
||||
qof
|
||||
qps
|
||||
rclt
|
||||
reimplementation
|
||||
reserialization
|
||||
reserialize
|
||||
reserializes
|
||||
rlig
|
||||
runtimes
|
||||
shcha
|
||||
slnt
|
||||
|
@ -50,8 +60,10 @@ TLDR
|
|||
tokenizes
|
||||
tonos
|
||||
tshe
|
||||
uiatextrange
|
||||
UIs
|
||||
und
|
||||
unregister
|
||||
versioned
|
||||
We'd
|
||||
wildcards
|
||||
|
|
|
@ -2,11 +2,13 @@ ACCEPTFILES
|
|||
ACCESSDENIED
|
||||
alignas
|
||||
alignof
|
||||
APPLYTOSUBMENUS
|
||||
bitfield
|
||||
bitfields
|
||||
BUILDBRANCH
|
||||
BUILDMSG
|
||||
BUILDNUMBER
|
||||
BYPOSITION
|
||||
charconv
|
||||
CLASSNOTAVAILABLE
|
||||
cmdletbinding
|
||||
|
@ -63,6 +65,7 @@ IObject
|
|||
iosfwd
|
||||
IPackage
|
||||
IPeasant
|
||||
isspace
|
||||
IStorage
|
||||
istream
|
||||
IStringable
|
||||
|
@ -77,7 +80,11 @@ llu
|
|||
localtime
|
||||
lround
|
||||
LSHIFT
|
||||
MENUCOMMAND
|
||||
MENUDATA
|
||||
MENUINFO
|
||||
memicmp
|
||||
mptt
|
||||
mov
|
||||
msappx
|
||||
MULTIPLEUSE
|
||||
|
@ -92,6 +99,7 @@ NOCHANGEDIR
|
|||
NOPROGRESS
|
||||
NOREDIRECTIONBITMAP
|
||||
NOREPEAT
|
||||
NOTIFYBYPOS
|
||||
NOTIFYICON
|
||||
NOTIFYICONDATA
|
||||
ntprivapi
|
||||
|
@ -132,11 +140,14 @@ SRWLOCK
|
|||
STDCPP
|
||||
STDMETHOD
|
||||
strchr
|
||||
strcpy
|
||||
streambuf
|
||||
strtoul
|
||||
Stubless
|
||||
Subheader
|
||||
Subpage
|
||||
syscall
|
||||
TASKBARCREATED
|
||||
TBPF
|
||||
THEMECHANGED
|
||||
tlg
|
||||
|
@ -151,6 +162,7 @@ userenv
|
|||
wcsstr
|
||||
wcstoui
|
||||
winmain
|
||||
wmemcmp
|
||||
wpc
|
||||
wsregex
|
||||
wwinmain
|
||||
|
|
|
@ -25,6 +25,7 @@ DWINRT
|
|||
enablewttlogging
|
||||
Intelli
|
||||
LKG
|
||||
Lxss
|
||||
mfcribbon
|
||||
microsoft
|
||||
microsoftonline
|
||||
|
|
|
@ -31,6 +31,7 @@ Kourosh
|
|||
kowalczyk
|
||||
leonmsft
|
||||
Lepilleur
|
||||
lhecker
|
||||
lukesampson
|
||||
Manandhar
|
||||
mbadolato
|
||||
|
@ -66,6 +67,7 @@ sonpham
|
|||
stakx
|
||||
thereses
|
||||
Walisch
|
||||
Wellons
|
||||
Wirt
|
||||
Wojciech
|
||||
zadjii
|
||||
|
|
|
@ -105,6 +105,7 @@ autoscrolling
|
|||
Autowrap
|
||||
AVerify
|
||||
AVI
|
||||
AVX
|
||||
awch
|
||||
azuredevopspodcast
|
||||
azzle
|
||||
|
@ -168,6 +169,7 @@ brandings
|
|||
BRK
|
||||
Browsable
|
||||
bsearch
|
||||
Bspace
|
||||
bstr
|
||||
BTNFACE
|
||||
buf
|
||||
|
@ -190,6 +192,7 @@ CARETBLINKINGENABLED
|
|||
CARRIAGERETURN
|
||||
cascadia
|
||||
cassert
|
||||
castsi
|
||||
catid
|
||||
cazamor
|
||||
CBash
|
||||
|
@ -268,9 +271,12 @@ cmder
|
|||
CMDEXT
|
||||
Cmdlet
|
||||
cmdline
|
||||
cmh
|
||||
CMOUSEBUTTONS
|
||||
cmp
|
||||
cmpeq
|
||||
cmt
|
||||
cmw
|
||||
cmyk
|
||||
CNL
|
||||
cnt
|
||||
|
@ -403,11 +409,13 @@ csbiex
|
|||
csharp
|
||||
CSHORT
|
||||
CSIDL
|
||||
Cspace
|
||||
csproj
|
||||
Csr
|
||||
csrmsg
|
||||
CSRSS
|
||||
csrutil
|
||||
css
|
||||
cstdarg
|
||||
cstddef
|
||||
cstdio
|
||||
|
@ -506,6 +514,8 @@ DECAWM
|
|||
DECCKM
|
||||
DECCOLM
|
||||
DECDHL
|
||||
decdld
|
||||
DECDLD
|
||||
DECDWL
|
||||
DECEKBD
|
||||
DECID
|
||||
|
@ -778,6 +788,7 @@ flyout
|
|||
fmodern
|
||||
fmtarg
|
||||
fmtid
|
||||
FNV
|
||||
FOLDERID
|
||||
FONTCHANGE
|
||||
fontdlg
|
||||
|
@ -786,6 +797,7 @@ FONTENUMPROC
|
|||
FONTFACE
|
||||
FONTFAMILY
|
||||
FONTHEIGHT
|
||||
FONTINFO
|
||||
fontlist
|
||||
FONTOK
|
||||
FONTSIZE
|
||||
|
@ -899,6 +911,7 @@ github
|
|||
gitlab
|
||||
gle
|
||||
globals
|
||||
GLYPHENTRY
|
||||
gmail
|
||||
GMEM
|
||||
GNUC
|
||||
|
@ -947,6 +960,7 @@ hdrstop
|
|||
HEIGHTSCROLL
|
||||
hfile
|
||||
hfont
|
||||
hfontresource
|
||||
hglobal
|
||||
hhh
|
||||
HHmm
|
||||
|
@ -1021,6 +1035,7 @@ IAction
|
|||
IApi
|
||||
IApplication
|
||||
IBase
|
||||
ICache
|
||||
icacls
|
||||
iccex
|
||||
icch
|
||||
|
@ -1260,6 +1275,7 @@ lnkd
|
|||
lnkfile
|
||||
LNM
|
||||
LOADONCALL
|
||||
loadu
|
||||
LOBYTE
|
||||
localappdata
|
||||
localhost
|
||||
|
@ -1267,6 +1283,7 @@ locsrc
|
|||
locstudio
|
||||
Loewen
|
||||
LOGFONT
|
||||
LOGFONTA
|
||||
LOGFONTW
|
||||
logissue
|
||||
lowercased
|
||||
|
@ -1420,6 +1437,7 @@ MOUSEFIRST
|
|||
MOUSEHWHEEL
|
||||
MOUSEMOVE
|
||||
mousewheel
|
||||
movemask
|
||||
MOVESTART
|
||||
msb
|
||||
msbuild
|
||||
|
@ -1452,6 +1470,7 @@ Mul
|
|||
multiline
|
||||
munged
|
||||
munges
|
||||
murmurhash
|
||||
mutex
|
||||
mutexes
|
||||
muxes
|
||||
|
@ -1502,6 +1521,7 @@ nfe
|
|||
nlength
|
||||
Nls
|
||||
NLSMODE
|
||||
nnn
|
||||
NOACTIVATE
|
||||
NOAPPLYNOW
|
||||
NOCLIP
|
||||
|
@ -1582,6 +1602,7 @@ NTVDM
|
|||
ntverp
|
||||
NTWIN
|
||||
nuget
|
||||
nullability
|
||||
nullness
|
||||
nullonfailure
|
||||
nullopt
|
||||
|
@ -1927,6 +1948,7 @@ realloc
|
|||
reamapping
|
||||
rects
|
||||
redef
|
||||
redefinable
|
||||
Redir
|
||||
redirector
|
||||
redist
|
||||
|
@ -1972,6 +1994,7 @@ rfc
|
|||
rftp
|
||||
rgb
|
||||
rgba
|
||||
RGBCOLOR
|
||||
rgbi
|
||||
rgci
|
||||
rgfae
|
||||
|
@ -2141,6 +2164,7 @@ SIGDN
|
|||
SINGLEFLAG
|
||||
SINGLETHREADED
|
||||
siup
|
||||
sixel
|
||||
SIZEBOX
|
||||
sizeof
|
||||
SIZESCROLL
|
||||
|
@ -2245,6 +2269,7 @@ SWMR
|
|||
SWP
|
||||
swprintf
|
||||
SYMED
|
||||
symlink
|
||||
SYNCPAINT
|
||||
sys
|
||||
syscalls
|
||||
|
@ -2528,6 +2553,7 @@ vcvarsall
|
|||
vcxitems
|
||||
vcxproj
|
||||
vec
|
||||
vectorized
|
||||
VERCTRL
|
||||
versioning
|
||||
VERTBAR
|
||||
|
@ -2745,6 +2771,7 @@ WTo
|
|||
wtof
|
||||
wtoi
|
||||
WTs
|
||||
WTSOFTFONT
|
||||
wtw
|
||||
wtypes
|
||||
Wubi
|
||||
|
@ -2774,6 +2801,7 @@ xes
|
|||
xff
|
||||
XFile
|
||||
XFORM
|
||||
xIcon
|
||||
XManifest
|
||||
XMath
|
||||
XMFLOAT
|
||||
|
@ -2806,6 +2834,7 @@ YCast
|
|||
YCENTER
|
||||
YCount
|
||||
YDPI
|
||||
yIcon
|
||||
yml
|
||||
YOffset
|
||||
YPosition
|
||||
|
|
|
@ -73,6 +73,7 @@ EndProject
|
|||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Host.unittest", "src\host\ut_lib\host.unittest.vcxproj", "{06EC74CB-9A12-429C-B551-8562EC954747}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{18D09A24-8240-42D6-8CB6-236EEE820263} = {18D09A24-8240-42D6-8CB6-236EEE820263}
|
||||
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF} = {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Host.Tests.Unit", "src\host\ut_host\Host.UnitTests.vcxproj", "{531C23E7-4B76-4C08-8AAD-04164CB628C9}"
|
||||
|
|
|
@ -1,48 +1,487 @@
|
|||
# This build should never run as CI or against a pull request.
|
||||
trigger: none
|
||||
pr: none
|
||||
|
||||
pool:
|
||||
name: WinDevPool-L
|
||||
demands: ImageOverride -equals WinDevVS16-latest
|
||||
|
||||
parameters:
|
||||
- name: branding
|
||||
displayName: "Branding (Build Type)"
|
||||
type: string
|
||||
default: Release
|
||||
values:
|
||||
- Release
|
||||
- Preview
|
||||
- name: buildTerminal
|
||||
displayName: "Build Windows Terminal MSIX"
|
||||
type: boolean
|
||||
default: true
|
||||
- name: buildTerminalVPack
|
||||
displayName: "Build Windows Terminal VPack"
|
||||
type: boolean
|
||||
default: false
|
||||
- name: buildWPF
|
||||
displayName: "Build Terminal WPF Control"
|
||||
type: boolean
|
||||
default: false
|
||||
- name: pgoBuildMode
|
||||
displayName: "PGO Build Mode"
|
||||
type: string
|
||||
default: Optimize
|
||||
values:
|
||||
- Optimize
|
||||
- Instrument
|
||||
- None
|
||||
|
||||
- name: buildConfigurations
|
||||
type: object
|
||||
default:
|
||||
- Release
|
||||
- name: buildPlatforms
|
||||
type: object
|
||||
default:
|
||||
- x64
|
||||
- x86
|
||||
- arm64
|
||||
|
||||
variables:
|
||||
baseYearForVersioning: 2019 # Used by build-console-int
|
||||
versionMajor: 0
|
||||
versionMinor: 1
|
||||
TerminalInternalPackageVersion: "0.0.7"
|
||||
|
||||
# When we move off PackageES for Versioning, we'll need to switch
|
||||
# name to this format. For now, though, we need to use DayOfYear.Rev
|
||||
# to unique our builds, as mandated by PackageES's Setup task.
|
||||
# name: '$(versionMajor).$(versionMinor).$(DayOfYear)$(Rev:r).0'
|
||||
#
|
||||
# Build name/version number above must end with .0 to make the
|
||||
# store publication machinery happy.
|
||||
name: 'Terminal_$(date:yyMM).$(date:dd)$(rev:rrr)'
|
||||
|
||||
# Build Arguments:
|
||||
# WindowsTerminalOfficialBuild=[true,false]
|
||||
# true - this is running on our build agent
|
||||
# false - running locally
|
||||
# WindowsTerminalBranding=[Dev,Preview,Release]
|
||||
# <none> - Development build resources (default)
|
||||
# Preview - Preview build resources
|
||||
# Release - regular build resources
|
||||
name: $(BuildDefinitionName)_$(date:yyMM).$(date:dd)$(rev:rrr)
|
||||
resources:
|
||||
repositories:
|
||||
- repository: self
|
||||
type: git
|
||||
ref: main
|
||||
jobs:
|
||||
- template: ./templates/build-console-audit-job.yml
|
||||
parameters:
|
||||
platform: x64
|
||||
- job: Build
|
||||
strategy:
|
||||
matrix:
|
||||
${{ each config in parameters.buildConfigurations }}:
|
||||
${{ each platform in parameters.buildPlatforms }}:
|
||||
${{ config }}_${{ platform }}:
|
||||
BuildConfiguration: ${{ config }}
|
||||
BuildPlatform: ${{ platform }}
|
||||
displayName: Build
|
||||
cancelTimeoutInMinutes: 1
|
||||
steps:
|
||||
- checkout: self
|
||||
clean: true
|
||||
submodules: true
|
||||
persistCredentials: True
|
||||
- task: PkgESSetupBuild@12
|
||||
displayName: Package ES - Setup Build
|
||||
inputs:
|
||||
disableOutputRedirect: true
|
||||
- task: PowerShell@2
|
||||
displayName: Rationalize Build Platform
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: >-
|
||||
$Arch = "$(BuildPlatform)"
|
||||
|
||||
- template: ./templates/build-console-int.yml
|
||||
parameters:
|
||||
platform: x64
|
||||
additionalBuildArguments: /p:WindowsTerminalOfficialBuild=true;WindowsTerminalBranding=Preview
|
||||
If ($Arch -Eq "x86") { $Arch = "Win32" }
|
||||
|
||||
- template: ./templates/build-console-int.yml
|
||||
parameters:
|
||||
platform: x86
|
||||
additionalBuildArguments: /p:WindowsTerminalOfficialBuild=true;WindowsTerminalBranding=Preview
|
||||
Write-Host "##vso[task.setvariable variable=RationalizedBuildPlatform]${Arch}"
|
||||
- task: NuGetToolInstaller@1
|
||||
displayName: Use NuGet 5.10
|
||||
inputs:
|
||||
versionSpec: 5.10
|
||||
- task: NuGetCommand@2
|
||||
displayName: NuGet custom
|
||||
inputs:
|
||||
command: custom
|
||||
selectOrConfig: config
|
||||
nugetConfigPath: NuGet.Config
|
||||
arguments: restore OpenConsole.sln -SolutionDirectory $(Build.SourcesDirectory)
|
||||
- task: UniversalPackages@0
|
||||
displayName: Download terminal-internal Universal Package
|
||||
inputs:
|
||||
feedListDownload: 2b3f8893-a6e8-411f-b197-a9e05576da48
|
||||
packageListDownload: e82d490c-af86-4733-9dc4-07b772033204
|
||||
versionListDownload: $(TerminalInternalPackageVersion)
|
||||
- task: TouchdownBuildTask@1
|
||||
displayName: Download Localization Files
|
||||
inputs:
|
||||
teamId: 7105
|
||||
authId: $(TouchdownAppId)
|
||||
authKey: $(TouchdownAppKey)
|
||||
resourceFilePath: >-
|
||||
src\cascadia\TerminalApp\Resources\en-US\Resources.resw
|
||||
|
||||
- template: ./templates/build-console-int.yml
|
||||
parameters:
|
||||
platform: arm64
|
||||
additionalBuildArguments: /p:WindowsTerminalOfficialBuild=true;WindowsTerminalBranding=Preview
|
||||
src\cascadia\TerminalControl\Resources\en-US\Resources.resw
|
||||
|
||||
- template: ./templates/check-formatting.yml
|
||||
src\cascadia\TerminalConnection\Resources\en-US\Resources.resw
|
||||
|
||||
- template: ./templates/release-sign-and-bundle.yml
|
||||
src\cascadia\TerminalSettingsModel\Resources\en-US\Resources.resw
|
||||
|
||||
src\cascadia\TerminalSettingsEditor\Resources\en-US\Resources.resw
|
||||
|
||||
src\cascadia\WindowsTerminalUniversal\Resources\en-US\Resources.resw
|
||||
|
||||
src\cascadia\CascadiaPackage\Resources\en-US\Resources.resw
|
||||
appendRelativeDir: true
|
||||
localizationTarget: false
|
||||
pseudoSetting: Included
|
||||
- task: PowerShell@2
|
||||
displayName: Move Loc files one level up
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: >-
|
||||
$Files = Get-ChildItem . -R -Filter 'Resources.resw' | ? FullName -Like '*en-US\*\Resources.resw'
|
||||
|
||||
$Files | % { Move-Item -Verbose $_.Directory $_.Directory.Parent.Parent -EA:Ignore }
|
||||
pwsh: true
|
||||
- task: PowerShell@2
|
||||
displayName: Generate NOTICE.html from NOTICE.md
|
||||
inputs:
|
||||
filePath: .\build\scripts\Generate-ThirdPartyNotices.ps1
|
||||
arguments: -MarkdownNoticePath .\NOTICE.md -OutputPath .\src\cascadia\CascadiaPackage\NOTICE.html
|
||||
pwsh: true
|
||||
- ${{ if eq(parameters.pgoBuildMode, 'Optimize') }}:
|
||||
- task: PowerShell@2
|
||||
displayName: Restore PGO Database
|
||||
inputs:
|
||||
filePath: tools/PGODatabase/restore-pgodb.ps1
|
||||
workingDirectory: $(Build.SourcesDirectory)\tools\PGODatabase
|
||||
- ${{ if eq(parameters.buildTerminal, true) }}:
|
||||
- task: VSBuild@1
|
||||
displayName: Build solution **\OpenConsole.sln
|
||||
inputs:
|
||||
solution: '**\OpenConsole.sln'
|
||||
vsVersion: 16.0
|
||||
msbuildArgs: /p:WindowsTerminalOfficialBuild=true /p:WindowsTerminalBranding=${{ parameters.branding }} /t:Terminal\CascadiaPackage;Terminal\WindowsTerminalUniversal /p:WindowsTerminalReleaseBuild=true /bl:$(Build.SourcesDirectory)\msbuild.binlog
|
||||
platform: $(BuildPlatform)
|
||||
configuration: $(BuildConfiguration)
|
||||
clean: true
|
||||
maximumCpuCount: true
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: 'Publish Artifact: binlog'
|
||||
condition: failed()
|
||||
continueOnError: True
|
||||
inputs:
|
||||
PathtoPublish: $(Build.SourcesDirectory)\msbuild.binlog
|
||||
ArtifactName: binlog-$(BuildPlatform)
|
||||
- ${{ if eq(parameters.pgoBuildMode, 'Optimize') }}:
|
||||
- task: PowerShell@2
|
||||
displayName: Validate binaries are optimized
|
||||
condition: and(succeeded(), eq(variables['BuildPlatform'], 'x64'))
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: >-
|
||||
$Binaries = 'OpenConsole.exe', 'WindowsTerminal.exe', 'TerminalApp.dll', 'TerminalConnection.dll', 'Microsoft.Terminal.Control.dll', 'Microsoft.Terminal.Remoting.dll', 'Microsoft.Terminal.Settings.Editor.dll', 'Microsoft.Terminal.Settings.Model.dll'
|
||||
|
||||
foreach ($BinFile in $Binaries) {
|
||||
|
||||
& "$(Build.SourcesDirectory)\tools\PGODatabase\verify-pgo.ps1" "$(Build.SourcesDirectory)/src/cascadia/CascadiaPackage/bin/$(BuildPlatform)/$(BuildConfiguration)/$BinFile"
|
||||
|
||||
}
|
||||
- task: PowerShell@2
|
||||
displayName: Check MSIX for common regressions
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: >-
|
||||
$Package = Get-ChildItem -Recurse -Filter "CascadiaPackage_*.msix"
|
||||
|
||||
.\build\scripts\Test-WindowsTerminalPackage.ps1 -Verbose -Path $Package.FullName
|
||||
pwsh: true
|
||||
- ${{ if eq(parameters.buildWPF, true) }}:
|
||||
- task: VSBuild@1
|
||||
displayName: Build solution **\OpenConsole.sln for PublicTerminalCore
|
||||
condition: and(succeeded(), ne(variables['BuildPlatform'], 'arm64'))
|
||||
inputs:
|
||||
solution: '**\OpenConsole.sln'
|
||||
vsVersion: 16.0
|
||||
msbuildArgs: /p:WindowsTerminalOfficialBuild=true /p:WindowsTerminalBranding=${{ parameters.branding }} /p:WindowsTerminalReleaseBuild=true /t:Terminal\wpf\PublicTerminalCore
|
||||
platform: $(BuildPlatform)
|
||||
configuration: $(BuildConfiguration)
|
||||
- task: PowerShell@2
|
||||
displayName: Source Index PDBs
|
||||
inputs:
|
||||
filePath: build\scripts\Index-Pdbs.ps1
|
||||
arguments: -SearchDir '$(Build.SourcesDirectory)' -SourceRoot '$(Build.SourcesDirectory)' -recursive -Verbose -CommitId $(Build.SourceVersion)
|
||||
errorActionPreference: silentlyContinue
|
||||
- task: ComponentGovernanceComponentDetection@0
|
||||
displayName: Component Detection
|
||||
- task: PowerShell@2
|
||||
displayName: Run Unit Tests
|
||||
condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x64'), eq(variables['BuildPlatform'], 'x86')))
|
||||
enabled: False
|
||||
inputs:
|
||||
filePath: build\scripts\Run-Tests.ps1
|
||||
arguments: -MatchPattern '*unit.test*.dll' -Platform '$(RationalizedBuildPlatform)' -Configuration '$(BuildConfiguration)'
|
||||
- task: PowerShell@2
|
||||
displayName: Run Feature Tests
|
||||
condition: and(succeeded(), eq(variables['BuildPlatform'], 'x64'))
|
||||
enabled: False
|
||||
inputs:
|
||||
filePath: build\scripts\Run-Tests.ps1
|
||||
arguments: -MatchPattern '*feature.test*.dll' -Platform '$(RationalizedBuildPlatform)' -Configuration '$(BuildConfiguration)'
|
||||
- ${{ if eq(parameters.buildTerminal, true) }}:
|
||||
- task: CopyFiles@2
|
||||
displayName: Copy *.appx/*.msix to Artifacts
|
||||
inputs:
|
||||
Contents: >-
|
||||
**/*.appx
|
||||
|
||||
**/*.msix
|
||||
|
||||
**/*.appxsym
|
||||
|
||||
!**/Microsoft.VCLibs*.appx
|
||||
TargetFolder: $(Build.ArtifactStagingDirectory)/appx
|
||||
OverWrite: true
|
||||
flattenFolders: true
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: Publish Artifact (appx)
|
||||
inputs:
|
||||
PathtoPublish: $(Build.ArtifactStagingDirectory)/appx
|
||||
ArtifactName: appx-$(BuildPlatform)-$(BuildConfiguration)
|
||||
- ${{ if eq(parameters.buildWPF, true) }}:
|
||||
- task: CopyFiles@2
|
||||
displayName: Copy PublicTerminalCore.dll to Artifacts
|
||||
condition: and(succeeded(), ne(variables['BuildPlatform'], 'arm64'))
|
||||
inputs:
|
||||
Contents: >-
|
||||
**/PublicTerminalCore.dll
|
||||
|
||||
**/api-ms-win-core-synch-l1-2-0.dll
|
||||
TargetFolder: $(Build.ArtifactStagingDirectory)/wpf
|
||||
OverWrite: true
|
||||
flattenFolders: true
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: Publish Artifact (PublicTerminalCore)
|
||||
condition: and(succeeded(), ne(variables['BuildPlatform'], 'arm64'))
|
||||
inputs:
|
||||
PathtoPublish: $(Build.ArtifactStagingDirectory)/wpf
|
||||
ArtifactName: wpf-dll-$(BuildPlatform)-$(BuildConfiguration)
|
||||
- task: PublishSymbols@2
|
||||
displayName: Publish symbols path
|
||||
continueOnError: True
|
||||
inputs:
|
||||
SearchPattern: '**/*.pdb'
|
||||
IndexSources: false
|
||||
SymbolServerType: TeamServices
|
||||
|
||||
- ${{ if eq(parameters.buildTerminal, true) }}:
|
||||
- job: BundleAndSign
|
||||
displayName: Create and sign AppX/MSIX bundles
|
||||
dependsOn: Build
|
||||
steps:
|
||||
- checkout: self
|
||||
clean: true
|
||||
submodules: true
|
||||
persistCredentials: True
|
||||
- task: PkgESSetupBuild@12
|
||||
displayName: Package ES - Setup Build
|
||||
inputs:
|
||||
disableOutputRedirect: true
|
||||
- task: DownloadBuildArtifacts@0
|
||||
displayName: Download Artifacts (*.appx, *.msix)
|
||||
inputs:
|
||||
downloadType: specific
|
||||
itemPattern: >-
|
||||
**/*.msix
|
||||
|
||||
**/*.appx
|
||||
extractTars: false
|
||||
- task: PowerShell@2
|
||||
displayName: Create WindowsTerminal*.msixbundle
|
||||
inputs:
|
||||
filePath: build\scripts\Create-AppxBundle.ps1
|
||||
arguments: -InputPath "$(System.ArtifactsDirectory)" -ProjectName CascadiaPackage -BundleVersion 0.0.0.0 -OutputPath "$(System.ArtifactsDirectory)\Microsoft.WindowsTerminal_$(XES_APPXMANIFESTVERSION)_8wekyb3d8bbwe.msixbundle"
|
||||
- task: PowerShell@2
|
||||
displayName: Create WindowsTerminalUniversal*.msixbundle
|
||||
inputs:
|
||||
filePath: build\scripts\Create-AppxBundle.ps1
|
||||
arguments: -InputPath "$(System.ArtifactsDirectory)" -ProjectName WindowsTerminalUniversal -BundleVersion $(XES_APPXMANIFESTVERSION) -OutputPath "$(System.ArtifactsDirectory)\Microsoft.WindowsTerminalUniversal_$(XES_APPXMANIFESTVERSION)_8wekyb3d8bbwe.msixbundle"
|
||||
- task: EsrpCodeSigning@1
|
||||
displayName: Submit *.msixbundle to ESRP for code signing
|
||||
inputs:
|
||||
ConnectedServiceName: 9d6d2960-0793-4d59-943e-78dcb434840a
|
||||
FolderPath: $(System.ArtifactsDirectory)
|
||||
Pattern: Microsoft.WindowsTerminal*.msixbundle
|
||||
UseMinimatch: true
|
||||
signConfigType: inlineSignParams
|
||||
inlineOperation: >-
|
||||
[
|
||||
{
|
||||
"KeyCode": "Dynamic",
|
||||
"CertTemplateName": "WINMSAPP1ST",
|
||||
"CertSubjectName": "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US",
|
||||
"OperationCode": "SigntoolSign",
|
||||
"Parameters": {
|
||||
"OpusName": "Microsoft",
|
||||
"OpusInfo": "http://www.microsoft.com",
|
||||
"FileDigest": "/fd \"SHA256\"",
|
||||
"TimeStamp": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
|
||||
},
|
||||
"ToolName": "sign",
|
||||
"ToolVersion": "1.0"
|
||||
},
|
||||
{
|
||||
"KeyCode": "Dynamic",
|
||||
"CertTemplateName": "WINMSAPP1ST",
|
||||
"CertSubjectName": "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US",
|
||||
"OperationCode": "SigntoolVerify",
|
||||
"Parameters": {},
|
||||
"ToolName": "sign",
|
||||
"ToolVersion": "1.0"
|
||||
}
|
||||
]
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: 'Publish Artifact: appxbundle-signed'
|
||||
inputs:
|
||||
PathtoPublish: $(System.ArtifactsDirectory)
|
||||
ArtifactName: appxbundle-signed
|
||||
|
||||
- ${{ if eq(parameters.buildWPF, true) }}:
|
||||
- job: PackageAndSignWPF
|
||||
strategy:
|
||||
matrix:
|
||||
${{ each config in parameters.buildConfigurations }}:
|
||||
${{ config }}:
|
||||
BuildConfiguration: ${{ config }}
|
||||
displayName: Create NuGet Package (WPF Terminal Control)
|
||||
dependsOn: Build
|
||||
steps:
|
||||
- checkout: self
|
||||
clean: true
|
||||
submodules: true
|
||||
persistCredentials: True
|
||||
- task: PkgESSetupBuild@12
|
||||
displayName: Package ES - Setup Build
|
||||
inputs:
|
||||
disableOutputRedirect: true
|
||||
- task: DownloadBuildArtifacts@0
|
||||
displayName: Download x86 PublicTerminalCore
|
||||
inputs:
|
||||
artifactName: wpf-dll-x86-$(BuildConfiguration)
|
||||
itemPattern: '**/*.dll'
|
||||
downloadPath: bin\Win32\$(BuildConfiguration)\
|
||||
extractTars: false
|
||||
- task: DownloadBuildArtifacts@0
|
||||
displayName: Download x64 PublicTerminalCore
|
||||
inputs:
|
||||
artifactName: wpf-dll-x64-$(BuildConfiguration)
|
||||
itemPattern: '**/*.dll'
|
||||
downloadPath: bin\x64\$(BuildConfiguration)\
|
||||
extractTars: false
|
||||
- task: PowerShell@2
|
||||
displayName: Move downloaded artifacts up a level
|
||||
inputs:
|
||||
targetType: inline
|
||||
# Find all artifact files and move them up a directory. Ugh.
|
||||
script: >-
|
||||
Get-ChildItem bin -Recurse -Directory -Filter wpf-dll-* | % {
|
||||
$_ | Get-ChildItem -Recurse -File | % {
|
||||
Move-Item -Verbose $_.FullName $_.Directory.Parent.FullName
|
||||
}
|
||||
}
|
||||
- task: NuGetToolInstaller@1
|
||||
displayName: Use NuGet 5.10.0
|
||||
inputs:
|
||||
versionSpec: 5.10.0
|
||||
- task: NuGetCommand@2
|
||||
displayName: NuGet restore copy
|
||||
inputs:
|
||||
selectOrConfig: config
|
||||
nugetConfigPath: NuGet.Config
|
||||
- task: VSBuild@1
|
||||
displayName: Build solution **\OpenConsole.sln for WPF Control
|
||||
inputs:
|
||||
solution: '**\OpenConsole.sln'
|
||||
vsVersion: 16.0
|
||||
msbuildArgs: /p:WindowsTerminalReleaseBuild=$(UseReleaseBranding);Version=$(XES_PACKAGEVERSIONNUMBER) /t:Pack
|
||||
platform: Any CPU
|
||||
configuration: $(BuildConfiguration)
|
||||
maximumCpuCount: true
|
||||
- task: PublishSymbols@2
|
||||
displayName: Publish symbols path
|
||||
continueOnError: True
|
||||
inputs:
|
||||
SearchPattern: '**/*.pdb'
|
||||
IndexSources: false
|
||||
SymbolServerType: TeamServices
|
||||
SymbolsArtifactName: Symbols_WPF_$(BuildConfiguration)
|
||||
- task: CopyFiles@2
|
||||
displayName: Copy *.nupkg to Artifacts
|
||||
inputs:
|
||||
Contents: '**/*Wpf*.nupkg'
|
||||
TargetFolder: $(Build.ArtifactStagingDirectory)/nupkg
|
||||
OverWrite: true
|
||||
flattenFolders: true
|
||||
- task: EsrpCodeSigning@1
|
||||
displayName: Submit *.nupkg to ESRP for code signing
|
||||
inputs:
|
||||
ConnectedServiceName: 9d6d2960-0793-4d59-943e-78dcb434840a
|
||||
FolderPath: $(Build.ArtifactStagingDirectory)/nupkg
|
||||
Pattern: '*.nupkg'
|
||||
UseMinimatch: true
|
||||
signConfigType: inlineSignParams
|
||||
inlineOperation: >-
|
||||
[
|
||||
{
|
||||
"KeyCode": "CP-401405",
|
||||
"OperationCode": "NuGetSign",
|
||||
"Parameters": {},
|
||||
"ToolName": "sign",
|
||||
"ToolVersion": "1.0"
|
||||
},
|
||||
{
|
||||
"KeyCode": "CP-401405",
|
||||
"OperationCode": "NuGetVerify",
|
||||
"Parameters": {},
|
||||
"ToolName": "sign",
|
||||
"ToolVersion": "1.0"
|
||||
}
|
||||
]
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: Publish Artifact (nupkg)
|
||||
inputs:
|
||||
PathtoPublish: $(Build.ArtifactStagingDirectory)\nupkg
|
||||
ArtifactName: wpf-nupkg-$(BuildConfiguration)
|
||||
|
||||
- ${{ if eq(parameters.buildTerminalVPack, true) }}:
|
||||
- job: VPack
|
||||
displayName: Create Windows vPack
|
||||
dependsOn: BundleAndSign
|
||||
steps:
|
||||
- checkout: self
|
||||
clean: true
|
||||
submodules: true
|
||||
- task: PkgESSetupBuild@12
|
||||
displayName: Package ES - Setup Build
|
||||
- task: DownloadBuildArtifacts@0
|
||||
displayName: Download Build Artifacts
|
||||
inputs:
|
||||
artifactName: appxbundle-signed
|
||||
extractTars: false
|
||||
- task: PowerShell@2
|
||||
displayName: Rename and stage packages for vpack
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: >-
|
||||
# Rename to known/fixed name for Windows build system
|
||||
|
||||
Get-ChildItem Microsoft.WindowsTerminal_*.msixbundle | Rename-Item -NewName { 'Microsoft.WindowsTerminal_8wekyb3d8bbwe.msixbundle' }
|
||||
|
||||
|
||||
# Create vpack directory and place item inside
|
||||
|
||||
mkdir WindowsTerminal.app
|
||||
|
||||
mv Microsoft.WindowsTerminal_8wekyb3d8bbwe.msixbundle .\WindowsTerminal.app\
|
||||
workingDirectory: $(System.ArtifactsDirectory)\appxbundle-signed
|
||||
- task: PkgESVPack@12
|
||||
displayName: 'Package ES - VPack'
|
||||
env:
|
||||
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
||||
inputs:
|
||||
sourceDirectory: $(System.ArtifactsDirectory)\appxbundle-signed\WindowsTerminal.app
|
||||
description: Windows Terminal pre-install application
|
||||
pushPkgName: WindowsTerminal.app
|
||||
owner: condev
|
||||
...
|
||||
|
|
|
@ -8,9 +8,12 @@ jobs:
|
|||
variables:
|
||||
BuildConfiguration: AuditMode
|
||||
BuildPlatform: ${{ parameters.platform }}
|
||||
pool: "windevbuildagents"
|
||||
# The public pool is also an option!
|
||||
# pool: { vmImage: windows-2019 }
|
||||
pool:
|
||||
${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPoolOSS-L
|
||||
${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPool-L
|
||||
demands: ImageOverride -equals WinDevVS16-latest
|
||||
|
||||
steps:
|
||||
- checkout: self
|
||||
|
|
|
@ -11,9 +11,12 @@ jobs:
|
|||
variables:
|
||||
BuildConfiguration: ${{ parameters.configuration }}
|
||||
BuildPlatform: ${{ parameters.platform }}
|
||||
pool: "windevbuildagents"
|
||||
# The public pool is also an option!
|
||||
# pool: { vmImage: windows-2019 }
|
||||
pool:
|
||||
${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPoolOSS-L
|
||||
${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPool-L
|
||||
demands: ImageOverride -equals WinDevVS16-latest
|
||||
|
||||
steps:
|
||||
- template: build-console-steps.yml
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
parameters:
|
||||
configuration: 'Release'
|
||||
platform: ''
|
||||
additionalBuildArguments: ''
|
||||
|
||||
jobs:
|
||||
- job: Build${{ parameters.platform }}${{ parameters.configuration }}
|
||||
displayName: Build ${{ parameters.platform }} ${{ parameters.configuration }}
|
||||
variables:
|
||||
BuildConfiguration: ${{ parameters.configuration }}
|
||||
BuildPlatform: ${{ parameters.platform }}
|
||||
PGOBuildMode: 'Optimize'
|
||||
|
||||
pool:
|
||||
name: Package ES Lab E
|
||||
demands:
|
||||
- msbuild
|
||||
- visualstudio
|
||||
- vstest
|
||||
|
||||
steps:
|
||||
- task: PkgESSetupBuild@10
|
||||
displayName: 'Package ES - Setup Build'
|
||||
inputs:
|
||||
useDfs: false
|
||||
productName: WindowsTerminal
|
||||
disableOutputRedirect: true
|
||||
|
||||
- template: build-console-steps.yml
|
||||
parameters:
|
||||
additionalBuildArguments: "/p:XesUseOneStoreVersioning=true;XesBaseYearForStoreVersion=$(baseYearForVersioning) ${{ parameters.additionalBuildArguments }}"
|
|
@ -12,9 +12,12 @@ jobs:
|
|||
BuildConfiguration: ${{ parameters.configuration }}
|
||||
BuildPlatform: ${{ parameters.platform }}
|
||||
PGOBuildMode: 'Instrument'
|
||||
pool: "windevbuildagents"
|
||||
# The public pool is also an option!
|
||||
# pool: { vmImage: windows-2019 }
|
||||
pool:
|
||||
${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPoolOSS-L
|
||||
${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPool-L
|
||||
demands: ImageOverride -equals WinDevVS16-latest
|
||||
|
||||
steps:
|
||||
- template: build-console-steps.yml
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
parameters:
|
||||
configuration: 'Release'
|
||||
|
||||
jobs:
|
||||
- job: SignDeploy${{ parameters.configuration }}
|
||||
displayName: Sign and Deploy for ${{ parameters.configuration }}
|
||||
|
||||
dependsOn:
|
||||
- Buildx64AuditMode
|
||||
- Buildx64Release
|
||||
- Buildx86Release
|
||||
- Buildarm64Release
|
||||
- CodeFormatCheck
|
||||
condition: |
|
||||
and
|
||||
(
|
||||
in(dependencies.Buildx64AuditMode.result, 'Succeeded', 'SucceededWithIssues', 'Skipped'),
|
||||
in(dependencies.Buildx64Release.result, 'Succeeded', 'SucceededWithIssues', 'Skipped'),
|
||||
in(dependencies.Buildx86Release.result, 'Succeeded', 'SucceededWithIssues', 'Skipped'),
|
||||
in(dependencies.Buildarm64Release.result, 'Succeeded', 'SucceededWithIssues', 'Skipped'),
|
||||
in(dependencies.CodeFormatCheck.result, 'Succeeded', 'SucceededWithIssues', 'Skipped')
|
||||
)
|
||||
|
||||
variables:
|
||||
BuildConfiguration: ${{ parameters.configuration }}
|
||||
AppxProjectName: CascadiaPackage
|
||||
AppxBundleName: Microsoft.WindowsTerminal_8wekyb3d8bbwe.msixbundle
|
||||
|
||||
pool:
|
||||
name: Package ES Lab E
|
||||
|
||||
steps:
|
||||
- checkout: self
|
||||
clean: true
|
||||
|
||||
- task: PkgESSetupBuild@10
|
||||
displayName: 'Package ES - Setup Build'
|
||||
inputs:
|
||||
useDfs: false
|
||||
productName: WindowsTerminal
|
||||
disableOutputRedirect: true
|
||||
|
||||
- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0
|
||||
displayName: 'Component Detection'
|
||||
|
||||
- task: DownloadBuildArtifacts@0
|
||||
displayName: Download AppX artifacts
|
||||
inputs:
|
||||
artifactName: 'appx-$(BuildConfiguration)'
|
||||
itemPattern: |
|
||||
**/*.appx
|
||||
**/*.msix
|
||||
downloadPath: '$(Build.ArtifactStagingDirectory)\appx'
|
||||
|
||||
- task: PowerShell@2
|
||||
displayName: 'Create $(AppxBundleName)'
|
||||
inputs:
|
||||
targetType: filePath
|
||||
filePath: '.\build\scripts\Create-AppxBundle.ps1'
|
||||
arguments: |
|
||||
-InputPath "$(Build.ArtifactStagingDirectory)\appx" -ProjectName $(AppxProjectName) -BundleVersion 0.0.0.0 -OutputPath "$(Build.ArtifactStagingDirectory)\$(AppxBundleName)"
|
||||
|
||||
- task: PkgESCodeSign@10
|
||||
displayName: 'Package ES - SignConfig.WindowsTerminal.xml'
|
||||
inputs:
|
||||
signConfigXml: 'build\config\SignConfig.WindowsTerminal.xml'
|
||||
inPathRoot: '$(Build.ArtifactStagingDirectory)'
|
||||
outPathRoot: '$(Build.ArtifactStagingDirectory)\signed'
|
||||
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: 'Publish Signed AppX'
|
||||
inputs:
|
||||
PathtoPublish: '$(Build.ArtifactStagingDirectory)\signed'
|
||||
ArtifactName: 'appxbundle-signed-$(BuildConfiguration)'
|
|
@ -4,13 +4,13 @@
|
|||
"title": "Microsoft's Windows Terminal Settings Profile Schema",
|
||||
"definitions": {
|
||||
"KeyChordSegment": {
|
||||
"pattern": "^(?<modifier>(?<mod1>ctrl|alt|shift|win)(?:\\+(?<mod2>ctrl|alt|shift|win)(?<!\\k<mod1>))?(?:\\+(?<mod3>ctrl|alt|shift|win)(?<!\\k<mod1>|\\k<mod2>))?(?:\\+(?<mod4>ctrl|alt|shift|win)(?<!\\k<mod1>|\\k<mod2>|\\k<mod3>))?\\+)?(?<key>[^\\s+]|app|menu|backspace|tab|enter|esc|escape|space|pgup|pageup|pgdn|pagedown|end|home|left|up|right|down|insert|delete|(?<!shift.+)(?:numpad_?[0-9]|numpad_(?:period|decimal))|numpad_(?:multiply|plus|add|minus|subtract|divide)|f[1-9]|f1[0-9]|f2[0-4]|plus)$",
|
||||
"pattern": "^(?:(?:ctrl|alt|shift|win)\\+)*(?:app|backspace|browser_(?:back|forward|refresh|stop|search|favorites|home)|comma|delete|down|end|enter|esc|escape|home|insert|left|menu|minus|pagedown|pageup|period|pgdn|pgup|plus|right|space|tab|up|f(?:1\\d?|2[0-4]?|[3-9])|numpad\\d|numpad_(?:\\d|add|decimal|divide|minus|multiply|period|plus|subtract)|(?:vk|sc)\\((?:[1-9]|1?\\d{2}|2[0-4]\\d|25[0-5])\\)|[^\\s+])(?:\\+(?:ctrl|alt|shift|win))*$",
|
||||
"type": "string",
|
||||
"description": "The string should fit the format \"[ctrl+][alt+][shift+][win+]<keyName>\", where each modifier is optional, separated by + symbols, and keyName is either one of the names listed in the table below, or any single key character. The string should be written in full lowercase.\napp, menu\tMENU key\nbackspace\tBACKSPACE key\ntab\tTAB key\nenter\tENTER key\nesc, escape\tESC key\nspace\tSPACEBAR\npgup, pageup\tPAGE UP key\npgdn, pagedown\tPAGE DOWN key\nend\tEND key\nhome\tHOME key\nleft\tLEFT ARROW key\nup\tUP ARROW key\nright\tRIGHT ARROW key\ndown\tDOWN ARROW key\ninsert\tINS key\ndelete\tDEL key\nnumpad_0-numpad_9, numpad0-numpad9\tNumeric keypad keys 0 to 9. Can't be combined with the shift modifier.\nnumpad_multiply\tNumeric keypad MULTIPLY key (*)\nnumpad_plus, numpad_add\tNumeric keypad ADD key (+)\nnumpad_minus, numpad_subtract\tNumeric keypad SUBTRACT key (-)\nnumpad_period, numpad_decimal\tNumeric keypad DECIMAL key (.). Can't be combined with the shift modifier.\nnumpad_divide\tNumeric keypad DIVIDE key (/)\nf1-f24\tF1 to F24 function keys\nplus\tADD key (+)"
|
||||
"description": "The string should fit the format \"[ctrl+][alt+][shift+][win+]<KeyName>\", where each modifier is optional. KeyName is either any single key character, an explicit virtual key or scan code in the form vk(nnn) and sc(nnn) respectively, or one of the special names listed at https://docs.microsoft.com/en-us/windows/terminal/customize-settings/actions#accepted-modifiers-and-keys"
|
||||
},
|
||||
"Color": {
|
||||
"default": "#",
|
||||
"pattern": "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$",
|
||||
"pattern": "^#[A-Fa-f0-9]{3}(?:[A-Fa-f0-9]{3})?$",
|
||||
"type": "string",
|
||||
"format": "color"
|
||||
},
|
||||
|
@ -154,6 +154,17 @@
|
|||
"description": "Sets how the background image aligns to the boundaries of the window when unfocused. Possible values: \"center\", \"left\", \"top\", \"right\", \"bottom\", \"topLeft\", \"topRight\", \"bottomLeft\", \"bottomRight\"",
|
||||
"type": "string"
|
||||
},
|
||||
"intenseTextStyle": {
|
||||
"default": "bright",
|
||||
"description": "Controls how 'intense' text is rendered. Values are \"bold\", \"bright\", \"all\" and \"none\"",
|
||||
"enum": [
|
||||
"none",
|
||||
"bold",
|
||||
"bright",
|
||||
"all"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"experimental.retroTerminalEffect": {
|
||||
"description": "When set to true, enable retro terminal effects when unfocused. This is an experimental feature, and its continued existence is not guaranteed.",
|
||||
"type": "boolean"
|
||||
|
@ -238,6 +249,8 @@
|
|||
"identifyWindow",
|
||||
"identifyWindows",
|
||||
"moveFocus",
|
||||
"movePane",
|
||||
"swapPane",
|
||||
"moveTab",
|
||||
"newTab",
|
||||
"newWindow",
|
||||
|
@ -270,6 +283,7 @@
|
|||
"toggleFocusMode",
|
||||
"toggleFullscreen",
|
||||
"togglePaneZoom",
|
||||
"toggleSplitOrientation",
|
||||
"toggleReadOnlyMode",
|
||||
"toggleShaderEffects",
|
||||
"wt",
|
||||
|
@ -283,7 +297,9 @@
|
|||
"right",
|
||||
"up",
|
||||
"down",
|
||||
"previous"
|
||||
"previous",
|
||||
"nextInOrder",
|
||||
"previousInOrder"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -491,6 +507,23 @@
|
|||
"type": "integer",
|
||||
"default": 0,
|
||||
"description": "Which tab to switch to, with the first being 0"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": [ "index" ]
|
||||
},
|
||||
"MovePaneAction": {
|
||||
"description": "Arguments corresponding to a Move Pane Action",
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/ShortcutAction" },
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type": "string", "pattern": "movePane" },
|
||||
"index": {
|
||||
"type": "integer",
|
||||
"default": 0,
|
||||
"description": "Which tab to move the pane to, with the first being 0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -507,7 +540,24 @@
|
|||
"direction": {
|
||||
"$ref": "#/definitions/FocusDirection",
|
||||
"default": "left",
|
||||
"description": "The direction to move focus in, between panes. Direction can be 'previous' to move to the most recently used pane."
|
||||
"description": "The direction to move focus in, between panes. Direction can be 'previous' to move to the most recently used pane, or 'nextInOrder' or 'previousInOrder' to move to the next or previous pane."
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": [ "direction" ]
|
||||
},
|
||||
"SwapPaneAction": {
|
||||
"description": "Arguments corresponding to a Swap Pane Action",
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/ShortcutAction" },
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type": "string", "pattern": "swapPane" },
|
||||
"direction": {
|
||||
"$ref": "#/definitions/FocusDirection",
|
||||
"default": "left",
|
||||
"description": "The direction to move the focus pane in, swapping panes. Direction can be 'previous' to swap with the most recently used pane, or 'nextInOrder' or 'previousInOrder' to move to the next or previous pane."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -596,7 +646,7 @@
|
|||
"defaultsFile",
|
||||
"allFiles",
|
||||
"settingsUI"
|
||||
|
||||
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -952,6 +1002,8 @@
|
|||
{ "$ref": "#/definitions/NewTabAction" },
|
||||
{ "$ref": "#/definitions/SwitchToTabAction" },
|
||||
{ "$ref": "#/definitions/MoveFocusAction" },
|
||||
{ "$ref": "#/definitions/MovePaneAction" },
|
||||
{ "$ref": "#/definitions/SwapPaneAction" },
|
||||
{ "$ref": "#/definitions/ResizePaneAction" },
|
||||
{ "$ref": "#/definitions/SendInputAction" },
|
||||
{ "$ref": "#/definitions/SplitPaneAction" },
|
||||
|
@ -1116,6 +1168,10 @@
|
|||
"description": "When set to true, we will use the software renderer (a.k.a. WARP) instead of the hardware one.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"experimental.input.forceVT": {
|
||||
"description": "Force the terminal to use the legacy input encoding. Certain keys in some applications may stop working when enabling this setting.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"initialCols": {
|
||||
"default": 120,
|
||||
"description": "The number of columns displayed in the window upon first load. If \"launchMode\" is set to \"maximized\" (or \"maximizedFocus\"), this property is ignored.",
|
||||
|
@ -1158,6 +1214,16 @@
|
|||
"minimum": 0,
|
||||
"type": [ "integer", "string" ],
|
||||
"deprecated": true
|
||||
},
|
||||
"minimizeToTray": {
|
||||
"default": "false",
|
||||
"description": "When set to true, minimizing a Terminal window will no longer appear in the taskbar. Instead, a Terminal icon will appear in the system tray through which the user can access their windows.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"alwaysShowTrayIcon": {
|
||||
"default": "false",
|
||||
"description": "When set to true, the Terminal's tray icon will always be shown in the system tray.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"actions": {
|
||||
"description": "Properties are specific to each custom action.",
|
||||
|
|
|
@ -140,12 +140,12 @@
|
|||
<!-- **END VC LIBS HACK** -->
|
||||
|
||||
<!-- This is required to get the package dependency in the AppXManifest. -->
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
</Target>
|
||||
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include "MyPage.h"
|
||||
#include <LibraryResources.h>
|
||||
#include "MyPage.g.cpp"
|
||||
#include "..\..\..\src\cascadia\UnitTests_Control\MockControlSettings.h"
|
||||
#include "MySettings.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
|
@ -26,17 +26,24 @@ namespace winrt::SampleApp::implementation
|
|||
|
||||
void MyPage::Create()
|
||||
{
|
||||
TerminalConnection::EchoConnection conn{};
|
||||
auto settings = winrt::make_self<ControlUnitTests::MockControlSettings>();
|
||||
auto settings = winrt::make_self<implementation::MySettings>();
|
||||
|
||||
auto connectionSettings{ TerminalConnection::ConptyConnection::CreateSettings(L"cmd.exe /k echo This TermControl is hosted in-proc...",
|
||||
winrt::hstring{},
|
||||
L"",
|
||||
nullptr,
|
||||
32,
|
||||
80,
|
||||
winrt::guid()) };
|
||||
|
||||
// "Microsoft.Terminal.TerminalConnection.ConptyConnection"
|
||||
winrt::hstring myClass{ winrt::name_of<TerminalConnection::ConptyConnection>() };
|
||||
TerminalConnection::ConnectionInformation connectInfo{ myClass, connectionSettings };
|
||||
|
||||
TerminalConnection::ITerminalConnection conn{ TerminalConnection::ConnectionInformation::CreateConnection(connectInfo) };
|
||||
Control::TermControl control{ *settings, conn };
|
||||
|
||||
InProcContent().Children().Append(control);
|
||||
|
||||
// Once the control loads (and not before that), write some text for debugging:
|
||||
control.Initialized([conn](auto&&, auto&&) {
|
||||
conn.WriteInput(L"This TermControl is hosted in-proc...");
|
||||
});
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
|
|
@ -19,10 +19,15 @@
|
|||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Button x:Name="TabRow"
|
||||
Grid.Row="0">
|
||||
"Tabs"
|
||||
</Button>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBox x:Name="GuidInput"
|
||||
Width="400"
|
||||
PlaceholderText="{}{guid here}" />
|
||||
<Button Grid.Row="0">
|
||||
Create
|
||||
</Button>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
<Grid x:Name="TabContent"
|
||||
Grid.Row="1"
|
||||
|
|
|
@ -4,6 +4,8 @@ Licensed under the MIT license.
|
|||
--*/
|
||||
#pragma once
|
||||
#include "../../inc/cppwinrt_utils.h"
|
||||
#include "../types/inc/colorTable.hpp"
|
||||
|
||||
#include <DefaultSettings.h>
|
||||
#include <conattrs.hpp>
|
||||
#include "MySettings.g.h"
|
||||
|
@ -12,9 +14,6 @@ namespace winrt::SampleApp::implementation
|
|||
{
|
||||
struct MySettings : MySettingsT<MySettings>
|
||||
{
|
||||
public:
|
||||
MySettings() = default;
|
||||
|
||||
// --------------------------- Core Settings ---------------------------
|
||||
// All of these settings are defined in ICoreSettings.
|
||||
|
||||
|
@ -88,6 +87,14 @@ namespace winrt::SampleApp::implementation
|
|||
winrt::Microsoft::Terminal::Core::Color GetColorTableEntry(int32_t index) noexcept { return _ColorTable.at(index); }
|
||||
std::array<winrt::Microsoft::Terminal::Core::Color, 16> ColorTable() { return _ColorTable; }
|
||||
void ColorTable(std::array<winrt::Microsoft::Terminal::Core::Color, 16> /*colors*/) {}
|
||||
|
||||
MySettings()
|
||||
{
|
||||
const auto campbellSpan = ::Microsoft::Console::Utils::CampbellColorTable();
|
||||
std::transform(campbellSpan.begin(), campbellSpan.end(), _ColorTable.begin(), [](auto&& color) {
|
||||
return static_cast<winrt::Microsoft::Terminal::Core::Color>(til::color{ color });
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -147,13 +147,13 @@
|
|||
<!-- ========================= Globals ======================== -->
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
|
||||
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" />
|
||||
</Target>
|
||||
|
||||
|
|
|
@ -48,6 +48,10 @@
|
|||
<Private>true</Private>
|
||||
<CopyLocalSatelliteAssemblies>true</CopyLocalSatelliteAssemblies>
|
||||
</ProjectReference>
|
||||
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\types\lib\types.vcxproj">
|
||||
<Project>{18D09A24-8240-42D6-8CB6-236EEE820263}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
@ -76,13 +80,13 @@
|
|||
</ItemGroup>
|
||||
|
||||
|
||||
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Import Project="$(OpenConsoleDir)packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" />
|
||||
</Target>
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.1.3" targetFramework="native" />
|
||||
<package id="Microsoft.UI.Xaml" version="2.5.0-prerelease.201202003" targetFramework="native" />
|
||||
<package id="Microsoft.UI.Xaml" version="2.6.2-prerelease.210818003" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
|
||||
</packages>
|
||||
|
|
|
@ -120,14 +120,14 @@
|
|||
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
|
||||
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.4\build\native\Microsoft.VCRTForwarders.140.targets" Condition="Exists('..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.4\build\native\Microsoft.VCRTForwarders.140.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.4\build\native\Microsoft.VCRTForwarders.140.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.4\build\native\Microsoft.VCRTForwarders.140.targets'))" />
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
|
||||
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.1.3" targetFramework="native" />
|
||||
<package id="Microsoft.UI.Xaml" version="2.5.0-prerelease.201202003" targetFramework="native" />
|
||||
<package id="Microsoft.UI.Xaml" version="2.6.2-prerelease.210818003" targetFramework="native" />
|
||||
<package id="Microsoft.VCRTForwarders.140" version="1.0.4" targetFramework="native" />
|
||||
</packages>
|
||||
|
|
|
@ -6,7 +6,7 @@ Module Name:
|
|||
- OutputCellView.hpp
|
||||
|
||||
Abstract:
|
||||
- Read-only view into a single cell of data that someone is attempting to write into the output buffer.
|
||||
- Read view into a single cell of data that someone is attempting to write into the output buffer.
|
||||
- This is done for performance reasons (avoid heap allocs and copies).
|
||||
|
||||
Author:
|
||||
|
@ -36,6 +36,21 @@ public:
|
|||
TextAttribute TextAttr() const noexcept;
|
||||
TextAttributeBehavior TextAttrBehavior() const noexcept;
|
||||
|
||||
void UpdateText(const std::wstring_view& view) noexcept
|
||||
{
|
||||
_view = view;
|
||||
};
|
||||
|
||||
void UpdateDbcsAttribute(const DbcsAttribute& dbcsAttr) noexcept
|
||||
{
|
||||
_dbcsAttr = dbcsAttr;
|
||||
}
|
||||
|
||||
void UpdateTextAttribute(const TextAttribute& textAttr) noexcept
|
||||
{
|
||||
_textAttr = textAttr;
|
||||
}
|
||||
|
||||
bool operator==(const OutputCellView& view) const noexcept;
|
||||
bool operator!=(const OutputCellView& view) const noexcept;
|
||||
|
||||
|
|
|
@ -95,16 +95,18 @@ bool TextAttribute::IsLegacy() const noexcept
|
|||
// - defaultFgColor: the default foreground color rgb value.
|
||||
// - defaultBgColor: the default background color rgb value.
|
||||
// - reverseScreenMode: true if the screen mode is reversed.
|
||||
// - blinkingIsFaint: true if blinking should be interpreted as faint.
|
||||
// - blinkingIsFaint: true if blinking should be interpreted as faint. (defaults to false)
|
||||
// - boldIsBright: true if "bold" should be interpreted as bright. (defaults to true)
|
||||
// Return Value:
|
||||
// - the foreground and background colors that should be displayed.
|
||||
std::pair<COLORREF, COLORREF> TextAttribute::CalculateRgbColors(const gsl::span<const COLORREF> colorTable,
|
||||
std::pair<COLORREF, COLORREF> TextAttribute::CalculateRgbColors(const std::array<COLORREF, 256>& colorTable,
|
||||
const COLORREF defaultFgColor,
|
||||
const COLORREF defaultBgColor,
|
||||
const bool reverseScreenMode,
|
||||
const bool blinkingIsFaint) const noexcept
|
||||
const bool blinkingIsFaint,
|
||||
const bool boldIsBright) const noexcept
|
||||
{
|
||||
auto fg = _foreground.GetColor(colorTable, defaultFgColor, IsBold());
|
||||
auto fg = _foreground.GetColor(colorTable, defaultFgColor, boldIsBright && IsBold());
|
||||
auto bg = _background.GetColor(colorTable, defaultBgColor);
|
||||
if (IsFaint() || (IsBlinking() && blinkingIsFaint))
|
||||
{
|
||||
|
|
|
@ -64,11 +64,12 @@ public:
|
|||
static TextAttribute StripErroneousVT16VersionsOfLegacyDefaults(const TextAttribute& attribute) noexcept;
|
||||
WORD GetLegacyAttributes() const noexcept;
|
||||
|
||||
std::pair<COLORREF, COLORREF> CalculateRgbColors(const gsl::span<const COLORREF> colorTable,
|
||||
std::pair<COLORREF, COLORREF> CalculateRgbColors(const std::array<COLORREF, 256>& colorTable,
|
||||
const COLORREF defaultFgColor,
|
||||
const COLORREF defaultBgColor,
|
||||
const bool reverseScreenMode = false,
|
||||
const bool blinkingIsFaint = false) const noexcept;
|
||||
const bool blinkingIsFaint = false,
|
||||
const bool boldIsBright = true) const noexcept;
|
||||
|
||||
bool IsLeadingByte() const noexcept;
|
||||
bool IsTrailingByte() const noexcept;
|
||||
|
|
|
@ -50,6 +50,9 @@ constexpr std::array<BYTE, 256> Index256ToIndex16 = {
|
|||
|
||||
// clang-format on
|
||||
|
||||
// We should only need 4B for TextColor. Any more than that is just waste.
|
||||
static_assert(sizeof(TextColor) == 4);
|
||||
|
||||
bool TextColor::CanBeBrightened() const noexcept
|
||||
{
|
||||
return IsIndex16() || IsDefault();
|
||||
|
@ -138,15 +141,12 @@ void TextColor::SetDefault() noexcept
|
|||
// - brighten: if true, we'll brighten a dark color table index.
|
||||
// Return Value:
|
||||
// - a COLORREF containing the real value of this TextColor.
|
||||
COLORREF TextColor::GetColor(gsl::span<const COLORREF> colorTable,
|
||||
const COLORREF defaultColor,
|
||||
bool brighten) const noexcept
|
||||
COLORREF TextColor::GetColor(const std::array<COLORREF, 256>& colorTable, const COLORREF defaultColor, bool brighten) const noexcept
|
||||
{
|
||||
if (IsDefault())
|
||||
{
|
||||
if (brighten)
|
||||
{
|
||||
FAIL_FAST_IF(colorTable.size() < 16);
|
||||
// See MSFT:20266024 for context on this fix.
|
||||
// Additionally todo MSFT:20271956 to fix this better for 19H2+
|
||||
// If we're a default color, check to see if the defaultColor exists
|
||||
|
@ -156,6 +156,61 @@ COLORREF TextColor::GetColor(gsl::span<const COLORREF> colorTable,
|
|||
// (Settings::_DefaultForeground==INVALID_COLOR, and the index
|
||||
// from _wFillAttribute is being used instead.)
|
||||
// If we find a match, return instead the bright version of this color
|
||||
|
||||
static_assert(sizeof(COLORREF) * 8 == 32, "The vectorized code broke. If you can't fix COLORREF, just remove the vectorized code.");
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1).
|
||||
#pragma warning(disable : 26490) // Don't use reinterpret_cast (type.1).
|
||||
#ifdef __AVX2__
|
||||
// I wrote this vectorized code one day, because the sun was shining so nicely.
|
||||
// There's no other reason for this to exist here, except for being pretty.
|
||||
// This code implements the exact same for loop you can find below, but is ~3x faster.
|
||||
//
|
||||
// A brief explanation for people unfamiliar with vectorized instructions:
|
||||
// Vectorized instructions, like "SSE" or "AVX", allow you to run
|
||||
// common operations like additions, multiplications, comparisons,
|
||||
// or bitwise operations concurrently on multiple values at once.
|
||||
//
|
||||
// We want to find the given defaultColor in the first 8 values of colorTable.
|
||||
// Coincidentally a COLORREF is a DWORD and 8 of them are exactly 256 bits.
|
||||
// -- The size of a single AVX register.
|
||||
//
|
||||
// Thus, the code works like this:
|
||||
// 1. Load all 8 DWORDs at once into one register
|
||||
// 2. Set the same defaultColor 8 times in another register
|
||||
// 3. Compare all 8 values at once
|
||||
// The result is either 0xffffffff or 0x00000000.
|
||||
// 4. Extract the most significant bit of each DWORD
|
||||
// Assuming that no duplicate colors exist in colorTable,
|
||||
// the result will be something like 0b00100000.
|
||||
// 5. Use BitScanForward (bsf) to find the index of the most significant 1 bit.
|
||||
const auto haystack = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(colorTable.data())); // 1.
|
||||
const auto needle = _mm256_set1_epi32(__builtin_bit_cast(int, defaultColor)); // 2.
|
||||
const auto result = _mm256_cmpeq_epi32(haystack, needle); // 3.
|
||||
const auto mask = _mm256_movemask_ps(_mm256_castsi256_ps(result)); // 4.
|
||||
unsigned long index;
|
||||
return _BitScanForward(&index, mask) ? til::at(colorTable, static_cast<size_t>(index) + 8) : defaultColor; // 5.
|
||||
#elif _M_AMD64
|
||||
// If you look closely this SSE2 algorithm is the same as the AVX one.
|
||||
// The two differences are that we need to:
|
||||
// * do everything twice, because SSE is limited to 128 bits and not 256.
|
||||
// * use _mm_packs_epi32 to merge two 128 bits vectors into one in step 3.5.
|
||||
// _mm_packs_epi32 takes two SSE registers and truncates all 8 DWORDs into 8 WORDs,
|
||||
// the latter of which fits into a single register (which is then used in the identical step 4).
|
||||
// * since the result are now 8 WORDs, we need to use _mm_movemask_epi8 (there's no 16-bit variant),
|
||||
// which unlike AVX's step 4 results in in something like 0b0000110000000000.
|
||||
// --> the index returned by _BitScanForward must be divided by 2.
|
||||
const auto haystack1 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(colorTable.data() + 0));
|
||||
const auto haystack2 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(colorTable.data() + 4));
|
||||
const auto needle = _mm_set1_epi32(__builtin_bit_cast(int, defaultColor));
|
||||
const auto result1 = _mm_cmpeq_epi32(haystack1, needle);
|
||||
const auto result2 = _mm_cmpeq_epi32(haystack2, needle);
|
||||
const auto result = _mm_packs_epi32(result1, result2); // 3.5
|
||||
const auto mask = _mm_movemask_epi8(result);
|
||||
unsigned long index;
|
||||
return _BitScanForward(&index, mask) ? til::at(colorTable, static_cast<size_t>(index / 2) + 8) : defaultColor;
|
||||
#else
|
||||
for (size_t i = 0; i < 8; i++)
|
||||
{
|
||||
if (til::at(colorTable, i) == defaultColor)
|
||||
|
@ -163,6 +218,8 @@ COLORREF TextColor::GetColor(gsl::span<const COLORREF> colorTable,
|
|||
return til::at(colorTable, i + 8);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
return defaultColor;
|
||||
|
@ -199,7 +256,7 @@ BYTE TextColor::GetLegacyIndex(const BYTE defaultIndex) const noexcept
|
|||
}
|
||||
else if (IsIndex256())
|
||||
{
|
||||
return Index256ToIndex16.at(GetIndex());
|
||||
return til::at(Index256ToIndex16, GetIndex());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -208,7 +265,7 @@ BYTE TextColor::GetLegacyIndex(const BYTE defaultIndex) const noexcept
|
|||
const BYTE compressedRgb = (_red & 0b11100000) +
|
||||
((_green >> 3) & 0b00011100) +
|
||||
((_blue >> 6) & 0b00000011);
|
||||
return CompressedRgbToIndex16.at(compressedRgb);
|
||||
return til::at(CompressedRgbToIndex16, compressedRgb);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -86,10 +86,7 @@ public:
|
|||
void SetIndex(const BYTE index, const bool isIndex256) noexcept;
|
||||
void SetDefault() noexcept;
|
||||
|
||||
COLORREF GetColor(gsl::span<const COLORREF> colorTable,
|
||||
const COLORREF defaultColor,
|
||||
const bool brighten = false) const noexcept;
|
||||
|
||||
COLORREF GetColor(const std::array<COLORREF, 256>& colorTable, const COLORREF defaultColor, bool brighten = false) const noexcept;
|
||||
BYTE GetLegacyIndex(const BYTE defaultIndex) const noexcept;
|
||||
|
||||
constexpr BYTE GetIndex() const noexcept
|
||||
|
@ -157,5 +154,3 @@ namespace WEX
|
|||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static_assert(sizeof(TextColor) <= 4 * sizeof(BYTE), "We should only need 4B for an entire TextColor. Any more than that is just waste");
|
||||
|
|
|
@ -315,6 +315,11 @@ void Cursor::StartDeferDrawing() noexcept
|
|||
_fDeferCursorRedraw = true;
|
||||
}
|
||||
|
||||
bool Cursor::IsDeferDrawing() noexcept
|
||||
{
|
||||
return _fDeferCursorRedraw;
|
||||
}
|
||||
|
||||
void Cursor::EndDeferDrawing() noexcept
|
||||
{
|
||||
if (_fHaveDeferredCursorRedraw)
|
||||
|
|
|
@ -55,6 +55,7 @@ public:
|
|||
const COLORREF GetColor() const noexcept;
|
||||
|
||||
void StartDeferDrawing() noexcept;
|
||||
bool IsDeferDrawing() noexcept;
|
||||
void EndDeferDrawing() noexcept;
|
||||
|
||||
void SetHasMoved(const bool fHasMoved) noexcept;
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
<ClInclude Include="..\Row.hpp" />
|
||||
<ClInclude Include="..\search.h" />
|
||||
<ClInclude Include="..\TextColor.h" />
|
||||
<ClInclude Include="..\TextAttribute.h" />
|
||||
<ClInclude Include="..\TextAttribute.hpp" />
|
||||
<ClInclude Include="..\textBuffer.hpp" />
|
||||
<ClInclude Include="..\textBufferCellIterator.hpp" />
|
||||
<ClInclude Include="..\textBufferTextIterator.hpp" />
|
||||
|
|
|
@ -418,7 +418,6 @@ bool TextBuffer::InsertCharacter(const std::wstring_view chars,
|
|||
|
||||
// Store character and double byte data
|
||||
CharRow& charRow = Row.GetCharRow();
|
||||
short const cBufferWidth = GetSize().Width();
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -1650,13 +1649,14 @@ const TextBuffer::TextAndColor TextBuffer::GetText(const bool includeCRLF,
|
|||
|
||||
if (!cell.DbcsAttr().IsTrailing())
|
||||
{
|
||||
selectionText.append(cell.Chars());
|
||||
const auto chars = cell.Chars();
|
||||
selectionText.append(chars);
|
||||
|
||||
if (copyTextColor)
|
||||
{
|
||||
const auto cellData = cell.TextAttr();
|
||||
const auto [CellFgAttr, CellBkAttr] = GetAttributeColors(cellData);
|
||||
for (const wchar_t wch : cell.Chars())
|
||||
for (size_t j = 0; j < chars.size(); ++j)
|
||||
{
|
||||
selectionFgAttr.push_back(CellFgAttr);
|
||||
selectionBkAttr.push_back(CellBkAttr);
|
||||
|
|
|
@ -94,20 +94,93 @@ bool TextBufferCellIterator::operator!=(const TextBufferCellIterator& it) const
|
|||
// - Reference to self after movement.
|
||||
TextBufferCellIterator& TextBufferCellIterator::operator+=(const ptrdiff_t& movement)
|
||||
{
|
||||
// Note that this method is called intensively when the terminal is under heavy load.
|
||||
// We need to aggressively optimize it, comparing to the -= operator.
|
||||
ptrdiff_t move = movement;
|
||||
auto newPos = _pos;
|
||||
while (move > 0 && !_exceeded)
|
||||
if (move < 0)
|
||||
{
|
||||
_exceeded = !_bounds.IncrementInBounds(newPos);
|
||||
// Early branching to leave the rare case to -= operator.
|
||||
// This helps reducing the instruction count within this method, which is good for instruction cache.
|
||||
return *this -= -move;
|
||||
}
|
||||
|
||||
// The remaining code in this function is functionally equivalent to:
|
||||
// auto newPos = _pos;
|
||||
// while (move > 0 && !_exceeded)
|
||||
// {
|
||||
// _exceeded = !_bounds.IncrementInBounds(newPos);
|
||||
// move--;
|
||||
// }
|
||||
// _SetPos(newPos);
|
||||
//
|
||||
// _SetPos() necessitates calling _GenerateView() and thus the construction
|
||||
// of a new OutputCellView(). This has a high performance impact (ICache spill?).
|
||||
// The code below inlines _bounds.IncrementInBounds as well as SetPos.
|
||||
// In the hot path (_pos.Y doesn't change) we modify the _view directly.
|
||||
|
||||
// Hoist these integers which will be used frequently later.
|
||||
const auto boundsRightInclusive = _bounds.RightInclusive();
|
||||
const auto boundsLeft = _bounds.Left();
|
||||
const auto boundsBottomInclusive = _bounds.BottomInclusive();
|
||||
const auto boundsTop = _bounds.Top();
|
||||
const auto oldX = _pos.X;
|
||||
const auto oldY = _pos.Y;
|
||||
|
||||
// Under MSVC writing the individual members of a COORD generates worse assembly
|
||||
// compared to having them be local variables. This causes a performance impact.
|
||||
auto newX = oldX;
|
||||
auto newY = oldY;
|
||||
|
||||
while (move > 0)
|
||||
{
|
||||
if (newX == boundsRightInclusive)
|
||||
{
|
||||
newX = boundsLeft;
|
||||
newY++;
|
||||
if (newY > boundsBottomInclusive)
|
||||
{
|
||||
newY = boundsTop;
|
||||
_exceeded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
newX++;
|
||||
_exceeded = false;
|
||||
}
|
||||
move--;
|
||||
}
|
||||
while (move < 0 && !_exceeded)
|
||||
|
||||
if (_exceeded)
|
||||
{
|
||||
_exceeded = !_bounds.DecrementInBounds(newPos);
|
||||
move++;
|
||||
// Early return because nothing needs to be done here.
|
||||
return *this;
|
||||
}
|
||||
_SetPos(newPos);
|
||||
return (*this);
|
||||
|
||||
if (newY == oldY)
|
||||
{
|
||||
// hot path
|
||||
const auto diff = gsl::narrow_cast<ptrdiff_t>(newX) - gsl::narrow_cast<ptrdiff_t>(oldX);
|
||||
_attrIter += diff;
|
||||
_view.UpdateTextAttribute(*_attrIter);
|
||||
|
||||
const CharRow& charRow = _pRow->GetCharRow();
|
||||
_view.UpdateText(charRow.GlyphAt(newX));
|
||||
_view.UpdateDbcsAttribute(charRow.DbcsAttrAt(newX));
|
||||
_pos.X = newX;
|
||||
}
|
||||
else
|
||||
{
|
||||
// cold path (_GenerateView is slow)
|
||||
_pRow = s_GetRow(_buffer, { newX, newY });
|
||||
_attrIter = _pRow->GetAttrRow().cbegin() + newX;
|
||||
_pos.X = newX;
|
||||
_pos.Y = newY;
|
||||
_GenerateView();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
@ -118,7 +191,22 @@ TextBufferCellIterator& TextBufferCellIterator::operator+=(const ptrdiff_t& move
|
|||
// - Reference to self after movement.
|
||||
TextBufferCellIterator& TextBufferCellIterator::operator-=(const ptrdiff_t& movement)
|
||||
{
|
||||
return this->operator+=(-movement);
|
||||
ptrdiff_t move = movement;
|
||||
if (move < 0)
|
||||
{
|
||||
return (*this) += (-move);
|
||||
}
|
||||
|
||||
auto newPos = _pos;
|
||||
while (move > 0 && !_exceeded)
|
||||
{
|
||||
_exceeded = !_bounds.DecrementInBounds(newPos);
|
||||
move--;
|
||||
}
|
||||
_SetPos(newPos);
|
||||
|
||||
_GenerateView();
|
||||
return (*this);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
|
|
@ -22,12 +22,11 @@ class TextAttributeTests
|
|||
TEST_METHOD(TestTextAttributeColorGetters);
|
||||
TEST_METHOD(TestReverseDefaultColors);
|
||||
TEST_METHOD(TestRoundtripDefaultColors);
|
||||
TEST_METHOD(TestBoldAsBright);
|
||||
|
||||
static const int COLOR_TABLE_SIZE = 16;
|
||||
COLORREF _colorTable[COLOR_TABLE_SIZE];
|
||||
std::array<COLORREF, 256> _colorTable;
|
||||
COLORREF _defaultFg = RGB(1, 2, 3);
|
||||
COLORREF _defaultBg = RGB(4, 5, 6);
|
||||
gsl::span<const COLORREF> _GetTableView();
|
||||
};
|
||||
|
||||
bool TextAttributeTests::ClassSetup()
|
||||
|
@ -51,11 +50,6 @@ bool TextAttributeTests::ClassSetup()
|
|||
return true;
|
||||
}
|
||||
|
||||
gsl::span<const COLORREF> TextAttributeTests::_GetTableView()
|
||||
{
|
||||
return gsl::span<const COLORREF>(&_colorTable[0], COLOR_TABLE_SIZE);
|
||||
}
|
||||
|
||||
void TextAttributeTests::TestRoundtripLegacy()
|
||||
{
|
||||
WORD expectedLegacy = FOREGROUND_BLUE | BACKGROUND_RED;
|
||||
|
@ -133,23 +127,22 @@ void TextAttributeTests::TestTextAttributeColorGetters()
|
|||
const COLORREF faintRed = RGB(127, 0, 0);
|
||||
const COLORREF green = RGB(0, 255, 0);
|
||||
TextAttribute attr(red, green);
|
||||
auto view = _GetTableView();
|
||||
|
||||
// verify that calculated foreground/background are the same as the direct
|
||||
// values when reverse video is not set
|
||||
VERIFY_IS_FALSE(attr.IsReverseVideo());
|
||||
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(view, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(view, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(red, green), attr.CalculateRgbColors(view, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(red, green), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
|
||||
// with reverse video set, calculated foreground/background values should be
|
||||
// switched while getters stay the same
|
||||
attr.SetReverseVideo(true);
|
||||
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(view, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(view, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(green, red), attr.CalculateRgbColors(view, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(green, red), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
|
||||
// reset the reverse video
|
||||
attr.SetReverseVideo(false);
|
||||
|
@ -158,17 +151,17 @@ void TextAttributeTests::TestTextAttributeColorGetters()
|
|||
// while the background and getters stay the same
|
||||
attr.SetFaint(true);
|
||||
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(view, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(view, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(faintRed, green), attr.CalculateRgbColors(view, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(faintRed, green), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
|
||||
// with reverse video set, calculated foreground/background values should be
|
||||
// switched, and the background fainter, while getters stay the same
|
||||
attr.SetReverseVideo(true);
|
||||
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(view, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(view, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(green, faintRed), attr.CalculateRgbColors(view, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(green, faintRed), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
|
||||
// reset the reverse video and faint attributes
|
||||
attr.SetReverseVideo(false);
|
||||
|
@ -178,17 +171,17 @@ void TextAttributeTests::TestTextAttributeColorGetters()
|
|||
// background, while getters stay the same
|
||||
attr.SetInvisible(true);
|
||||
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(view, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(view, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(green, green), attr.CalculateRgbColors(view, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(green, green), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
|
||||
// with reverse video set, the calculated background value should match
|
||||
// the foreground, while getters stay the same
|
||||
attr.SetReverseVideo(true);
|
||||
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(view, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(view, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(red, red), attr.CalculateRgbColors(view, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(red, red), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
}
|
||||
|
||||
void TextAttributeTests::TestReverseDefaultColors()
|
||||
|
@ -196,40 +189,39 @@ void TextAttributeTests::TestReverseDefaultColors()
|
|||
const COLORREF red = RGB(255, 0, 0);
|
||||
const COLORREF green = RGB(0, 255, 0);
|
||||
TextAttribute attr{};
|
||||
auto view = _GetTableView();
|
||||
|
||||
// verify that calculated foreground/background are the same as the direct
|
||||
// values when reverse video is not set
|
||||
VERIFY_IS_FALSE(attr.IsReverseVideo());
|
||||
|
||||
VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(view, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(view, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(view, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
|
||||
// with reverse video set, calculated foreground/background values should be
|
||||
// switched while getters stay the same
|
||||
attr.SetReverseVideo(true);
|
||||
VERIFY_IS_TRUE(attr.IsReverseVideo());
|
||||
|
||||
VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(view, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(view, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultBg, _defaultFg), attr.CalculateRgbColors(view, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultBg, _defaultFg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
|
||||
attr.SetForeground(red);
|
||||
VERIFY_IS_TRUE(attr.IsReverseVideo());
|
||||
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(view, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(view, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultBg, red), attr.CalculateRgbColors(view, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultBg, red), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
|
||||
attr.Invert();
|
||||
VERIFY_IS_FALSE(attr.IsReverseVideo());
|
||||
attr.SetDefaultForeground();
|
||||
attr.SetBackground(green);
|
||||
|
||||
VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(view, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(view, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, green), attr.CalculateRgbColors(view, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, green), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
}
|
||||
|
||||
void TextAttributeTests::TestRoundtripDefaultColors()
|
||||
|
@ -272,3 +264,56 @@ void TextAttributeTests::TestRoundtripDefaultColors()
|
|||
// Reset the legacy default colors to white on black.
|
||||
TextAttribute::SetLegacyDefaultAttributes(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
|
||||
}
|
||||
|
||||
void TextAttributeTests::TestBoldAsBright()
|
||||
{
|
||||
const COLORREF darkBlack = til::at(_colorTable, 0);
|
||||
const COLORREF brightBlack = til::at(_colorTable, 8);
|
||||
const COLORREF darkGreen = til::at(_colorTable, 2);
|
||||
|
||||
TextAttribute attr{};
|
||||
|
||||
// verify that calculated foreground/background are the same as the direct
|
||||
// values when not bold
|
||||
VERIFY_IS_FALSE(attr.IsBold());
|
||||
|
||||
VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false));
|
||||
|
||||
// with bold set, calculated foreground/background values shouldn't change for the default colors.
|
||||
attr.SetBold(true);
|
||||
VERIFY_IS_TRUE(attr.IsBold());
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false));
|
||||
|
||||
attr.SetIndexedForeground(0);
|
||||
VERIFY_IS_TRUE(attr.IsBold());
|
||||
|
||||
Log::Comment(L"Foreground should be bright black when bold is bright is enabled");
|
||||
VERIFY_ARE_EQUAL(std::make_pair(brightBlack, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true));
|
||||
|
||||
Log::Comment(L"Foreground should be dark black when bold is bright is disabled");
|
||||
VERIFY_ARE_EQUAL(std::make_pair(darkBlack, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false));
|
||||
|
||||
attr.SetIndexedBackground(2);
|
||||
VERIFY_IS_TRUE(attr.IsBold());
|
||||
|
||||
Log::Comment(L"background should be unaffected by 'bold is bright'");
|
||||
VERIFY_ARE_EQUAL(std::make_pair(brightBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(darkBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false));
|
||||
|
||||
attr.SetBold(false);
|
||||
VERIFY_IS_FALSE(attr.IsBold());
|
||||
Log::Comment(L"when not bold, 'bold is bright' changes nothing");
|
||||
VERIFY_ARE_EQUAL(std::make_pair(darkBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(darkBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false));
|
||||
|
||||
Log::Comment(L"When set to a bright color, and bold, 'bold is bright' changes nothing");
|
||||
attr.SetBold(true);
|
||||
attr.SetIndexedForeground(8);
|
||||
VERIFY_IS_TRUE(attr.IsBold());
|
||||
VERIFY_ARE_EQUAL(std::make_pair(brightBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(brightBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false));
|
||||
}
|
||||
|
|
|
@ -23,11 +23,9 @@ class TextColorTests
|
|||
TEST_METHOD(TestRgbColor);
|
||||
TEST_METHOD(TestChangeColor);
|
||||
|
||||
static const int COLOR_TABLE_SIZE = 16;
|
||||
COLORREF _colorTable[COLOR_TABLE_SIZE];
|
||||
std::array<COLORREF, 256> _colorTable;
|
||||
COLORREF _defaultFg = RGB(1, 2, 3);
|
||||
COLORREF _defaultBg = RGB(4, 5, 6);
|
||||
gsl::span<const COLORREF> _GetTableView();
|
||||
};
|
||||
|
||||
bool TextColorTests::ClassSetup()
|
||||
|
@ -51,11 +49,6 @@ bool TextColorTests::ClassSetup()
|
|||
return true;
|
||||
}
|
||||
|
||||
gsl::span<const COLORREF> TextColorTests::_GetTableView()
|
||||
{
|
||||
return gsl::span<const COLORREF>(&_colorTable[0], COLOR_TABLE_SIZE);
|
||||
}
|
||||
|
||||
void TextColorTests::TestDefaultColor()
|
||||
{
|
||||
TextColor defaultColor;
|
||||
|
@ -64,18 +57,16 @@ void TextColorTests::TestDefaultColor()
|
|||
VERIFY_IS_FALSE(defaultColor.IsLegacy());
|
||||
VERIFY_IS_FALSE(defaultColor.IsRgb());
|
||||
|
||||
auto view = _GetTableView();
|
||||
|
||||
auto color = defaultColor.GetColor(view, _defaultFg, false);
|
||||
auto color = defaultColor.GetColor(_colorTable, _defaultFg, false);
|
||||
VERIFY_ARE_EQUAL(_defaultFg, color);
|
||||
|
||||
color = defaultColor.GetColor(view, _defaultFg, true);
|
||||
color = defaultColor.GetColor(_colorTable, _defaultFg, true);
|
||||
VERIFY_ARE_EQUAL(_defaultFg, color);
|
||||
|
||||
color = defaultColor.GetColor(view, _defaultBg, false);
|
||||
color = defaultColor.GetColor(_colorTable, _defaultBg, false);
|
||||
VERIFY_ARE_EQUAL(_defaultBg, color);
|
||||
|
||||
color = defaultColor.GetColor(view, _defaultBg, true);
|
||||
color = defaultColor.GetColor(_colorTable, _defaultBg, true);
|
||||
VERIFY_ARE_EQUAL(_defaultBg, color);
|
||||
}
|
||||
|
||||
|
@ -87,18 +78,16 @@ void TextColorTests::TestDarkIndexColor()
|
|||
VERIFY_IS_TRUE(indexColor.IsLegacy());
|
||||
VERIFY_IS_FALSE(indexColor.IsRgb());
|
||||
|
||||
auto view = _GetTableView();
|
||||
|
||||
auto color = indexColor.GetColor(view, _defaultFg, false);
|
||||
auto color = indexColor.GetColor(_colorTable, _defaultFg, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[7], color);
|
||||
|
||||
color = indexColor.GetColor(view, _defaultFg, true);
|
||||
color = indexColor.GetColor(_colorTable, _defaultFg, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = indexColor.GetColor(view, _defaultBg, false);
|
||||
color = indexColor.GetColor(_colorTable, _defaultBg, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[7], color);
|
||||
|
||||
color = indexColor.GetColor(view, _defaultBg, true);
|
||||
color = indexColor.GetColor(_colorTable, _defaultBg, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
}
|
||||
|
||||
|
@ -110,18 +99,16 @@ void TextColorTests::TestBrightIndexColor()
|
|||
VERIFY_IS_TRUE(indexColor.IsLegacy());
|
||||
VERIFY_IS_FALSE(indexColor.IsRgb());
|
||||
|
||||
auto view = _GetTableView();
|
||||
|
||||
auto color = indexColor.GetColor(view, _defaultFg, false);
|
||||
auto color = indexColor.GetColor(_colorTable, _defaultFg, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = indexColor.GetColor(view, _defaultFg, true);
|
||||
color = indexColor.GetColor(_colorTable, _defaultFg, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = indexColor.GetColor(view, _defaultBg, false);
|
||||
color = indexColor.GetColor(_colorTable, _defaultBg, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = indexColor.GetColor(view, _defaultBg, true);
|
||||
color = indexColor.GetColor(_colorTable, _defaultBg, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
}
|
||||
|
||||
|
@ -134,18 +121,16 @@ void TextColorTests::TestRgbColor()
|
|||
VERIFY_IS_FALSE(rgbColor.IsLegacy());
|
||||
VERIFY_IS_TRUE(rgbColor.IsRgb());
|
||||
|
||||
auto view = _GetTableView();
|
||||
|
||||
auto color = rgbColor.GetColor(view, _defaultFg, false);
|
||||
auto color = rgbColor.GetColor(_colorTable, _defaultFg, false);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultFg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, true);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultBg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, false);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultBg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, true);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
}
|
||||
|
||||
|
@ -158,57 +143,55 @@ void TextColorTests::TestChangeColor()
|
|||
VERIFY_IS_FALSE(rgbColor.IsLegacy());
|
||||
VERIFY_IS_TRUE(rgbColor.IsRgb());
|
||||
|
||||
auto view = _GetTableView();
|
||||
|
||||
auto color = rgbColor.GetColor(view, _defaultFg, false);
|
||||
auto color = rgbColor.GetColor(_colorTable, _defaultFg, false);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultFg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, true);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultBg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, false);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultBg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, true);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
|
||||
rgbColor.SetDefault();
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultFg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, false);
|
||||
VERIFY_ARE_EQUAL(_defaultFg, color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultFg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, true);
|
||||
VERIFY_ARE_EQUAL(_defaultFg, color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultBg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, false);
|
||||
VERIFY_ARE_EQUAL(_defaultBg, color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultBg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, true);
|
||||
VERIFY_ARE_EQUAL(_defaultBg, color);
|
||||
|
||||
rgbColor.SetIndex(7, false);
|
||||
color = rgbColor.GetColor(view, _defaultFg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[7], color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultFg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultBg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[7], color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultBg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
rgbColor.SetIndex(15, false);
|
||||
color = rgbColor.GetColor(view, _defaultFg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultFg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultBg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultBg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
}
|
||||
|
|
|
@ -146,12 +146,12 @@
|
|||
<!-- **END VC LIBS HACK** -->
|
||||
|
||||
<!-- This is required to get the package dependency in the AppXManifest. -->
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
</Target>
|
||||
|
||||
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />
|
||||
|
|
|
@ -397,6 +397,10 @@ namespace SettingsModelLocalTests
|
|||
"name":"action6",
|
||||
"command": { "action": "newWindow", "startingDirectory":"C:\\foo", "commandline": "bar.exe" }
|
||||
},
|
||||
{
|
||||
"name":"action7_startingDirectoryWithTrailingSlash",
|
||||
"command": { "action": "newWindow", "startingDirectory":"C:\\", "commandline": "bar.exe" }
|
||||
},
|
||||
])" };
|
||||
|
||||
const auto commands0Json = VerifyParseSucceeded(commands0String);
|
||||
|
@ -405,7 +409,7 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_ARE_EQUAL(0u, commands.Size());
|
||||
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
|
||||
VERIFY_ARE_EQUAL(0u, warnings.size());
|
||||
VERIFY_ARE_EQUAL(7u, commands.Size());
|
||||
VERIFY_ARE_EQUAL(8u, commands.Size());
|
||||
|
||||
{
|
||||
auto command = commands.Lookup(L"action0");
|
||||
|
@ -503,5 +507,20 @@ namespace SettingsModelLocalTests
|
|||
L"cmdline: \"%s\"", cmdline.c_str()));
|
||||
VERIFY_ARE_EQUAL(L"--startingDirectory \"C:\\foo\" -- \"bar.exe\"", terminalArgs.ToCommandline());
|
||||
}
|
||||
|
||||
{
|
||||
auto command = commands.Lookup(L"action7_startingDirectoryWithTrailingSlash");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action());
|
||||
const auto& realArgs = command.ActionAndArgs().Args().try_as<NewWindowArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& terminalArgs = realArgs.TerminalArgs();
|
||||
VERIFY_IS_NOT_NULL(terminalArgs);
|
||||
auto cmdline = terminalArgs.ToCommandline();
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"cmdline: \"%s\"", cmdline.c_str()));
|
||||
VERIFY_ARE_EQUAL(L"--startingDirectory \"C:\\\\\" -- \"bar.exe\"", terminalArgs.ToCommandline());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1971,9 +1971,9 @@ namespace SettingsModelLocalTests
|
|||
auto settings = implementation::CascadiaSettings::FromJson(settingsObject);
|
||||
|
||||
VERIFY_ARE_EQUAL(3u, settings->_globals->_actionMap->_KeyMap.size());
|
||||
VERIFY_IS_NULL(settings->_globals->_actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('a') }));
|
||||
VERIFY_IS_NULL(settings->_globals->_actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('b') }));
|
||||
VERIFY_IS_NULL(settings->_globals->_actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('c') }));
|
||||
VERIFY_IS_NULL(settings->_globals->_actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('A'), 0 }));
|
||||
VERIFY_IS_NULL(settings->_globals->_actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('B'), 0 }));
|
||||
VERIFY_IS_NULL(settings->_globals->_actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('C'), 0 }));
|
||||
|
||||
for (const auto& warning : settings->_globals->_keybindingsWarnings)
|
||||
{
|
||||
|
@ -2124,7 +2124,7 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_ARE_EQUAL(1u, nameMap.Size());
|
||||
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('A') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('A'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
|
@ -2141,7 +2141,7 @@ namespace SettingsModelLocalTests
|
|||
Log::Comment(L"Note that we're skipping ctrl+B, since that doesn't have `keys` set.");
|
||||
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('C'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
|
@ -2155,7 +2155,7 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('D') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('D'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
|
@ -2169,7 +2169,7 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('E') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('E'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
|
@ -2183,7 +2183,7 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('F') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('F'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
|
@ -2841,7 +2841,7 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
|
||||
VERIFY_ARE_EQUAL(1u, nameMap.Size());
|
||||
|
||||
const KeyChord expectedKeyChord{ true, false, true, static_cast<int>('W') };
|
||||
const KeyChord expectedKeyChord{ true, false, true, false, static_cast<int>('W'), 0 };
|
||||
{
|
||||
// Verify NameMap returns correct value
|
||||
const auto& cmd{ nameMap.TryLookup(L"foo") };
|
||||
|
|
|
@ -36,10 +36,15 @@ namespace SettingsModelLocalTests
|
|||
TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml")
|
||||
END_TEST_CLASS()
|
||||
|
||||
TEST_METHOD(KeyChords);
|
||||
TEST_METHOD(ManyKeysSameAction);
|
||||
TEST_METHOD(LayerKeybindings);
|
||||
TEST_METHOD(UnbindKeybindings);
|
||||
|
||||
TEST_METHOD(LayerScancodeKeybindings);
|
||||
|
||||
TEST_METHOD(TestExplicitUnbind);
|
||||
|
||||
TEST_METHOD(TestArbitraryArgs);
|
||||
TEST_METHOD(TestSplitPaneArgs);
|
||||
|
||||
|
@ -53,6 +58,7 @@ namespace SettingsModelLocalTests
|
|||
TEST_METHOD(TestMoveTabArgs);
|
||||
|
||||
TEST_METHOD(TestGetKeyBindingForAction);
|
||||
TEST_METHOD(KeybindingsWithoutVkey);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
|
@ -61,6 +67,69 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
};
|
||||
|
||||
void KeyBindingsTests::KeyChords()
|
||||
{
|
||||
struct testCase
|
||||
{
|
||||
VirtualKeyModifiers modifiers;
|
||||
int32_t vkey;
|
||||
int32_t scanCode;
|
||||
std::wstring_view expected;
|
||||
};
|
||||
|
||||
static constexpr std::array testCases{
|
||||
testCase{
|
||||
VirtualKeyModifiers::None,
|
||||
'A',
|
||||
0,
|
||||
L"a",
|
||||
},
|
||||
testCase{
|
||||
VirtualKeyModifiers::Control,
|
||||
'A',
|
||||
0,
|
||||
L"ctrl+a",
|
||||
},
|
||||
testCase{
|
||||
VirtualKeyModifiers::Control | VirtualKeyModifiers::Shift,
|
||||
VK_OEM_PLUS,
|
||||
0,
|
||||
L"ctrl+shift+plus",
|
||||
},
|
||||
testCase{
|
||||
VirtualKeyModifiers::Control | VirtualKeyModifiers::Menu | VirtualKeyModifiers::Shift | VirtualKeyModifiers::Windows,
|
||||
255,
|
||||
0,
|
||||
L"win+ctrl+alt+shift+vk(255)",
|
||||
},
|
||||
testCase{
|
||||
VirtualKeyModifiers::Control | VirtualKeyModifiers::Menu | VirtualKeyModifiers::Shift | VirtualKeyModifiers::Windows,
|
||||
0,
|
||||
123,
|
||||
L"win+ctrl+alt+shift+sc(123)",
|
||||
},
|
||||
};
|
||||
|
||||
for (const auto& tc : testCases)
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(L"Testing case:\"%s\"", tc.expected.data()));
|
||||
|
||||
const auto actualString = KeyChordSerialization::ToString({ tc.modifiers, tc.vkey, tc.scanCode });
|
||||
VERIFY_ARE_EQUAL(tc.expected, actualString);
|
||||
|
||||
auto expectedVkey = tc.vkey;
|
||||
if (!expectedVkey)
|
||||
{
|
||||
expectedVkey = MapVirtualKeyW(tc.scanCode, MAPVK_VSC_TO_VK_EX);
|
||||
}
|
||||
|
||||
const auto actualKeyChord = KeyChordSerialization::FromString(actualString);
|
||||
VERIFY_ARE_EQUAL(tc.modifiers, actualKeyChord.Modifiers());
|
||||
VERIFY_ARE_EQUAL(expectedVkey, actualKeyChord.Vkey());
|
||||
VERIFY_ARE_EQUAL(tc.scanCode, actualKeyChord.ScanCode());
|
||||
}
|
||||
}
|
||||
|
||||
void KeyBindingsTests::ManyKeysSameAction()
|
||||
{
|
||||
const std::string bindings0String{ R"([ { "command": "copy", "keys": ["ctrl+c"] } ])" };
|
||||
|
@ -75,7 +144,6 @@ namespace SettingsModelLocalTests
|
|||
const auto bindings2Json = VerifyParseSucceeded(bindings2String);
|
||||
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(actionMap);
|
||||
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
|
||||
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
|
@ -99,7 +167,6 @@ namespace SettingsModelLocalTests
|
|||
const auto bindings2Json = VerifyParseSucceeded(bindings2String);
|
||||
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(actionMap);
|
||||
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
|
||||
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
|
@ -129,7 +196,6 @@ namespace SettingsModelLocalTests
|
|||
const auto bindings5Json = VerifyParseSucceeded(bindings5String);
|
||||
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(actionMap);
|
||||
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
|
||||
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
|
@ -142,7 +208,7 @@ namespace SettingsModelLocalTests
|
|||
L"Try unbinding a key using `\"unbound\"` to unbind the key"));
|
||||
actionMap->LayerJson(bindings2Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('c') }));
|
||||
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('C'), 0 }));
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Try unbinding a key using `null` to unbind the key"));
|
||||
|
@ -152,7 +218,7 @@ namespace SettingsModelLocalTests
|
|||
// Then try layering in the bad setting
|
||||
actionMap->LayerJson(bindings3Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('c') }));
|
||||
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('C'), 0 }));
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Try unbinding a key using an unrecognized command to unbind the key"));
|
||||
|
@ -162,7 +228,7 @@ namespace SettingsModelLocalTests
|
|||
// Then try layering in the bad setting
|
||||
actionMap->LayerJson(bindings4Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('c') }));
|
||||
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('C'), 0 }));
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Try unbinding a key using a straight up invalid value to unbind the key"));
|
||||
|
@ -172,13 +238,38 @@ namespace SettingsModelLocalTests
|
|||
// Then try layering in the bad setting
|
||||
actionMap->LayerJson(bindings5Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('c') }));
|
||||
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('C'), 0 }));
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Try unbinding a key that wasn't bound at all"));
|
||||
actionMap->LayerJson(bindings2Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('c') }));
|
||||
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('C'), 0 }));
|
||||
}
|
||||
|
||||
void KeyBindingsTests::TestExplicitUnbind()
|
||||
{
|
||||
const std::string bindings0String{ R"([ { "command": "copy", "keys": ["ctrl+c"] } ])" };
|
||||
const std::string bindings1String{ R"([ { "command": "unbound", "keys": ["ctrl+c"] } ])" };
|
||||
const std::string bindings2String{ R"([ { "command": "copy", "keys": ["ctrl+c"] } ])" };
|
||||
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
const auto bindings1Json = VerifyParseSucceeded(bindings1String);
|
||||
const auto bindings2Json = VerifyParseSucceeded(bindings2String);
|
||||
|
||||
const KeyChord keyChord{ VirtualKeyModifiers::Control, static_cast<int32_t>('C'), 0 };
|
||||
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_FALSE(actionMap->IsKeyChordExplicitlyUnbound(keyChord));
|
||||
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
VERIFY_IS_FALSE(actionMap->IsKeyChordExplicitlyUnbound(keyChord));
|
||||
|
||||
actionMap->LayerJson(bindings1Json);
|
||||
VERIFY_IS_TRUE(actionMap->IsKeyChordExplicitlyUnbound(keyChord));
|
||||
|
||||
actionMap->LayerJson(bindings2Json);
|
||||
VERIFY_IS_FALSE(actionMap->IsKeyChordExplicitlyUnbound(keyChord));
|
||||
}
|
||||
|
||||
void KeyBindingsTests::TestArbitraryArgs()
|
||||
|
@ -203,7 +294,6 @@ namespace SettingsModelLocalTests
|
|||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(actionMap);
|
||||
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(10u, actionMap->_KeyMap.size());
|
||||
|
@ -211,10 +301,9 @@ namespace SettingsModelLocalTests
|
|||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `copy` without args parses as Copy(SingleLine=false)"));
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('C'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<CopyTextArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_FALSE(realArgs.SingleLine());
|
||||
}
|
||||
|
@ -222,10 +311,9 @@ namespace SettingsModelLocalTests
|
|||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `copy` with args parses them correctly"));
|
||||
KeyChord kc{ true, false, true, static_cast<int32_t>('C') };
|
||||
KeyChord kc{ true, false, true, false, static_cast<int32_t>('C'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<CopyTextArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_FALSE(realArgs.SingleLine());
|
||||
}
|
||||
|
@ -233,10 +321,9 @@ namespace SettingsModelLocalTests
|
|||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `copy` with args parses them correctly"));
|
||||
KeyChord kc{ false, true, true, static_cast<int32_t>('C') };
|
||||
KeyChord kc{ false, true, true, false, static_cast<int32_t>('C'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<CopyTextArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_TRUE(realArgs.SingleLine());
|
||||
}
|
||||
|
@ -244,11 +331,10 @@ namespace SettingsModelLocalTests
|
|||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `newTab` without args parses as NewTab(Index=null)"));
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('T') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('T'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<NewTabArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_NULL(realArgs.TerminalArgs().ProfileIndex());
|
||||
|
@ -256,11 +342,10 @@ namespace SettingsModelLocalTests
|
|||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `newTab` parses args correctly"));
|
||||
KeyChord kc{ true, false, true, static_cast<int32_t>('T') };
|
||||
KeyChord kc{ true, false, true, false, static_cast<int32_t>('T'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<NewTabArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs().ProfileIndex());
|
||||
|
@ -270,11 +355,10 @@ namespace SettingsModelLocalTests
|
|||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `newTab` with an index greater than the legacy "
|
||||
L"args afforded parses correctly"));
|
||||
KeyChord kc{ true, false, true, static_cast<int32_t>('Y') };
|
||||
KeyChord kc{ true, false, true, false, static_cast<int32_t>('Y'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<NewTabArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs().ProfileIndex());
|
||||
|
@ -284,11 +368,10 @@ namespace SettingsModelLocalTests
|
|||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `copy` ignores args it doesn't understand"));
|
||||
KeyChord kc{ true, false, true, static_cast<int32_t>('B') };
|
||||
KeyChord kc{ true, false, true, false, static_cast<int32_t>('B'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::CopyText, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<CopyTextArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_FALSE(realArgs.SingleLine());
|
||||
}
|
||||
|
@ -296,11 +379,10 @@ namespace SettingsModelLocalTests
|
|||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `copy` null as it's `args` parses as the default option"));
|
||||
KeyChord kc{ true, false, true, static_cast<int32_t>('B') };
|
||||
KeyChord kc{ true, false, true, false, static_cast<int32_t>('B'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::CopyText, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<CopyTextArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_FALSE(realArgs.SingleLine());
|
||||
}
|
||||
|
@ -308,11 +390,10 @@ namespace SettingsModelLocalTests
|
|||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `adjustFontSize` with a positive delta parses args correctly"));
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('F') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('F'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::AdjustFontSize, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<AdjustFontSizeArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<AdjustFontSizeArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(1, realArgs.Delta());
|
||||
}
|
||||
|
@ -320,11 +401,10 @@ namespace SettingsModelLocalTests
|
|||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `adjustFontSize` with a negative delta parses args correctly"));
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('G') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('G'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::AdjustFontSize, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<AdjustFontSizeArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<AdjustFontSizeArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(-1, realArgs.Delta());
|
||||
}
|
||||
|
@ -342,44 +422,39 @@ namespace SettingsModelLocalTests
|
|||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(actionMap);
|
||||
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(4u, actionMap->_KeyMap.size());
|
||||
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('D') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('D'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<SplitPaneArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('E') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('E'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<SplitPaneArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('G') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('G'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<SplitPaneArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('H') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('H'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<SplitPaneArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
|
||||
}
|
||||
|
@ -396,37 +471,33 @@ namespace SettingsModelLocalTests
|
|||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(actionMap);
|
||||
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(3u, actionMap->_KeyMap.size());
|
||||
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('C'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SetTabColor, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SetTabColorArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<SetTabColorArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NULL(realArgs.TabColor());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('D') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('D'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SetTabColor, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SetTabColorArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<SetTabColorArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NOT_NULL(realArgs.TabColor());
|
||||
// Remember that COLORREFs are actually BBGGRR order, while the string is in #RRGGBB order
|
||||
VERIFY_ARE_EQUAL(til::color(0x563412), til::color(realArgs.TabColor().Value()));
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('F') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('F'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SetTabColor, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SetTabColorArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<SetTabColorArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NULL(realArgs.TabColor());
|
||||
}
|
||||
|
@ -441,16 +512,14 @@ namespace SettingsModelLocalTests
|
|||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(actionMap);
|
||||
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('C'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<CopyTextArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_FALSE(realArgs.SingleLine());
|
||||
}
|
||||
|
@ -470,63 +539,56 @@ namespace SettingsModelLocalTests
|
|||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(actionMap);
|
||||
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(6u, actionMap->_KeyMap.size());
|
||||
|
||||
{
|
||||
KeyChord kc{ false, false, false, static_cast<int32_t>(VK_UP) };
|
||||
KeyChord kc{ false, false, false, false, static_cast<int32_t>(VK_UP), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::ScrollUp, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<ScrollUpArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<ScrollUpArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NULL(realArgs.RowsToScroll());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ false, false, false, static_cast<int32_t>(VK_DOWN) };
|
||||
KeyChord kc{ false, false, false, false, static_cast<int32_t>(VK_DOWN), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::ScrollDown, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<ScrollDownArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<ScrollDownArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NULL(realArgs.RowsToScroll());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>(VK_UP) };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>(VK_UP), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::ScrollUp, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<ScrollUpArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<ScrollUpArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NULL(realArgs.RowsToScroll());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>(VK_DOWN) };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>(VK_DOWN), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::ScrollDown, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<ScrollDownArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<ScrollDownArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NULL(realArgs.RowsToScroll());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, true, static_cast<int32_t>(VK_UP) };
|
||||
KeyChord kc{ true, false, true, false, static_cast<int32_t>(VK_UP), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::ScrollUp, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<ScrollUpArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<ScrollUpArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NOT_NULL(realArgs.RowsToScroll());
|
||||
VERIFY_ARE_EQUAL(10u, realArgs.RowsToScroll().Value());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, true, static_cast<int32_t>(VK_DOWN) };
|
||||
KeyChord kc{ true, false, true, false, static_cast<int32_t>(VK_DOWN), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::ScrollDown, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<ScrollDownArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<ScrollDownArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NOT_NULL(realArgs.RowsToScroll());
|
||||
VERIFY_ARE_EQUAL(10u, realArgs.RowsToScroll().Value());
|
||||
|
@ -535,7 +597,6 @@ namespace SettingsModelLocalTests
|
|||
const std::string bindingsInvalidString{ R"([{ "keys": ["up"], "command": { "action": "scrollDown", "rowsToScroll": -1 } }])" };
|
||||
const auto bindingsInvalidJson = VerifyParseSucceeded(bindingsInvalidString);
|
||||
auto invalidActionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(invalidActionMap);
|
||||
VERIFY_ARE_EQUAL(0u, invalidActionMap->_KeyMap.size());
|
||||
VERIFY_THROWS(invalidActionMap->LayerJson(bindingsInvalidJson);, std::exception);
|
||||
}
|
||||
|
@ -551,26 +612,23 @@ namespace SettingsModelLocalTests
|
|||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(actionMap);
|
||||
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(2u, actionMap->_KeyMap.size());
|
||||
|
||||
{
|
||||
KeyChord kc{ false, false, false, static_cast<int32_t>(VK_UP) };
|
||||
KeyChord kc{ false, false, false, false, static_cast<int32_t>(VK_UP), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::MoveTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<MoveTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<MoveTabArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(realArgs.Direction(), MoveTabDirection::Forward);
|
||||
}
|
||||
{
|
||||
KeyChord kc{ false, false, false, static_cast<int32_t>(VK_DOWN) };
|
||||
KeyChord kc{ false, false, false, false, static_cast<int32_t>(VK_DOWN), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::MoveTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<MoveTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<MoveTabArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(realArgs.Direction(), MoveTabDirection::Backward);
|
||||
}
|
||||
|
@ -584,7 +642,6 @@ namespace SettingsModelLocalTests
|
|||
const std::string bindingsInvalidString{ R"([{ "keys": ["up"], "command": { "action": "moveTab", "direction": "bad" } }])" };
|
||||
const auto bindingsInvalidJson = VerifyParseSucceeded(bindingsInvalidString);
|
||||
auto invalidActionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(invalidActionMap);
|
||||
VERIFY_ARE_EQUAL(0u, invalidActionMap->_KeyMap.size());
|
||||
VERIFY_THROWS(invalidActionMap->LayerJson(bindingsInvalidJson);, std::exception);
|
||||
}
|
||||
|
@ -601,35 +658,31 @@ namespace SettingsModelLocalTests
|
|||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(actionMap);
|
||||
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(3u, actionMap->_KeyMap.size());
|
||||
|
||||
{
|
||||
KeyChord kc{ false, false, false, static_cast<int32_t>(VK_UP) };
|
||||
KeyChord kc{ false, false, false, false, static_cast<int32_t>(VK_UP), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::ToggleCommandPalette, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<ToggleCommandPaletteArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<ToggleCommandPaletteArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(realArgs.LaunchMode(), CommandPaletteLaunchMode::Action);
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>(VK_UP) };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>(VK_UP), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::ToggleCommandPalette, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<ToggleCommandPaletteArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<ToggleCommandPaletteArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(realArgs.LaunchMode(), CommandPaletteLaunchMode::Action);
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, true, static_cast<int32_t>(VK_UP) };
|
||||
KeyChord kc{ true, false, true, false, static_cast<int32_t>(VK_UP), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::ToggleCommandPalette, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<ToggleCommandPaletteArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<ToggleCommandPaletteArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(realArgs.LaunchMode(), CommandPaletteLaunchMode::CommandLine);
|
||||
}
|
||||
|
@ -637,7 +690,6 @@ namespace SettingsModelLocalTests
|
|||
const std::string bindingsInvalidString{ R"([{ "keys": ["up"], "command": { "action": "commandPalette", "launchMode": "bad" } }])" };
|
||||
const auto bindingsInvalidJson = VerifyParseSucceeded(bindingsInvalidString);
|
||||
auto invalidActionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(invalidActionMap);
|
||||
VERIFY_ARE_EQUAL(0u, invalidActionMap->_KeyMap.size());
|
||||
VERIFY_THROWS(invalidActionMap->LayerJson(bindingsInvalidJson);, std::exception);
|
||||
}
|
||||
|
@ -669,7 +721,6 @@ namespace SettingsModelLocalTests
|
|||
};
|
||||
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(actionMap);
|
||||
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
|
||||
|
||||
{
|
||||
|
@ -677,7 +728,7 @@ namespace SettingsModelLocalTests
|
|||
actionMap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
const auto& kbd{ actionMap->GetKeyBindingForAction(ShortcutAction::CloseWindow) };
|
||||
VerifyKeyChordEquality({ VirtualKeyModifiers::Control, static_cast<int32_t>('A') }, kbd);
|
||||
VerifyKeyChordEquality({ VirtualKeyModifiers::Control, static_cast<int32_t>('A'), 0 }, kbd);
|
||||
}
|
||||
{
|
||||
Log::Comment(L"command with args");
|
||||
|
@ -688,7 +739,7 @@ namespace SettingsModelLocalTests
|
|||
args->SingleLine(true);
|
||||
|
||||
const auto& kbd{ actionMap->GetKeyBindingForAction(ShortcutAction::CopyText, *args) };
|
||||
VerifyKeyChordEquality({ VirtualKeyModifiers::Control, static_cast<int32_t>('B') }, kbd);
|
||||
VerifyKeyChordEquality({ VirtualKeyModifiers::Control, static_cast<int32_t>('B'), 0 }, kbd);
|
||||
}
|
||||
{
|
||||
Log::Comment(L"command with new terminal args");
|
||||
|
@ -700,7 +751,7 @@ namespace SettingsModelLocalTests
|
|||
auto args{ winrt::make_self<implementation::NewTabArgs>(*newTerminalArgs) };
|
||||
|
||||
const auto& kbd{ actionMap->GetKeyBindingForAction(ShortcutAction::NewTab, *args) };
|
||||
VerifyKeyChordEquality({ VirtualKeyModifiers::Control, static_cast<int32_t>('C') }, kbd);
|
||||
VerifyKeyChordEquality({ VirtualKeyModifiers::Control, static_cast<int32_t>('C'), 0 }, kbd);
|
||||
}
|
||||
{
|
||||
Log::Comment(L"command with hidden args");
|
||||
|
@ -708,7 +759,45 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_ARE_EQUAL(4u, actionMap->_KeyMap.size());
|
||||
|
||||
const auto& kbd{ actionMap->GetKeyBindingForAction(ShortcutAction::ToggleCommandPalette) };
|
||||
VerifyKeyChordEquality({ VirtualKeyModifiers::Control | VirtualKeyModifiers::Shift, static_cast<int32_t>('P') }, kbd);
|
||||
VerifyKeyChordEquality({ VirtualKeyModifiers::Control | VirtualKeyModifiers::Shift, static_cast<int32_t>('P'), 0 }, kbd);
|
||||
}
|
||||
}
|
||||
|
||||
void KeyBindingsTests::LayerScancodeKeybindings()
|
||||
{
|
||||
Log::Comment(L"Layering a keybinding with a character literal on top of"
|
||||
L" an equivalent sc() key should replace it.");
|
||||
|
||||
// Wrap the first one in `R"!(...)!"` because it has `()` internally.
|
||||
const std::string bindings0String{ R"!([ { "command": "quakeMode", "keys":"win+sc(41)" } ])!" };
|
||||
const std::string bindings1String{ R"([ { "keys": "win+`", "command": { "action": "globalSummon", "monitor": "any" } } ])" };
|
||||
const std::string bindings2String{ R"([ { "keys": "ctrl+shift+`", "command": { "action": "quakeMode" } } ])" };
|
||||
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
const auto bindings1Json = VerifyParseSucceeded(bindings1String);
|
||||
const auto bindings2Json = VerifyParseSucceeded(bindings2String);
|
||||
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
|
||||
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
|
||||
actionMap->LayerJson(bindings1Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size(), L"Layering the second action should replace the first one.");
|
||||
|
||||
actionMap->LayerJson(bindings2Json);
|
||||
VERIFY_ARE_EQUAL(2u, actionMap->_KeyMap.size());
|
||||
}
|
||||
|
||||
void KeyBindingsTests::KeybindingsWithoutVkey()
|
||||
{
|
||||
const auto json = VerifyParseSucceeded(R"!([{"command": "quakeMode", "keys":"shift+sc(255)"}])!");
|
||||
|
||||
const auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
actionMap->LayerJson(json);
|
||||
|
||||
const auto action = actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Shift, 0, 255 });
|
||||
VERIFY_IS_NOT_NULL(action);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,10 +98,10 @@
|
|||
<!-- From Microsoft.UI.Xaml.targets -->
|
||||
<Native-Platform Condition="'$(Platform)' == 'Win32'">x86</Native-Platform>
|
||||
<Native-Platform Condition="'$(Platform)' != 'Win32'">$(Platform)</Native-Platform>
|
||||
<_MUXBinRoot>"$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\runtimes\win10-$(Native-Platform)\native\"</_MUXBinRoot>
|
||||
<_MUXBinRoot>"$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\runtimes\win10-$(Native-Platform)\native\"</_MUXBinRoot>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- We actually can just straight up reference MUX here, it's fine -->
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -37,7 +37,7 @@ namespace SettingsModelLocalTests
|
|||
|
||||
TEST_METHOD(TestTerminalArgsForBinding);
|
||||
|
||||
TEST_METHOD(MakeSettingsForProfileThatDoesntExist);
|
||||
TEST_METHOD(MakeSettingsForProfile);
|
||||
TEST_METHOD(MakeSettingsForDefaultProfileThatDoesntExist);
|
||||
|
||||
TEST_METHOD(TestLayerProfileOnColorScheme);
|
||||
|
@ -113,7 +113,7 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_ARE_EQUAL(12u, actionMapImpl->_KeyMap.size());
|
||||
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('A') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('A'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
|
@ -126,15 +126,15 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid0, guid);
|
||||
VERIFY_ARE_EQUAL(guid0, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('B') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('B'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
|
@ -148,15 +148,15 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid1, guid);
|
||||
VERIFY_ARE_EQUAL(guid1, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(2, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('C'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
|
@ -170,15 +170,15 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid1, guid);
|
||||
VERIFY_ARE_EQUAL(guid1, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(2, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('D') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('D'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
|
@ -192,15 +192,15 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(profile2Guid, guid);
|
||||
VERIFY_ARE_EQUAL(profile2Guid, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(3, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('E') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('E'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
|
@ -214,15 +214,15 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", realArgs.TerminalArgs().Commandline());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid0, guid);
|
||||
VERIFY_ARE_EQUAL(guid0, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('F') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('F'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
|
@ -237,15 +237,15 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", realArgs.TerminalArgs().Commandline());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid1, guid);
|
||||
VERIFY_ARE_EQUAL(guid1, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(2, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('G') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('G'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
|
@ -257,15 +257,15 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid0, guid);
|
||||
VERIFY_ARE_EQUAL(guid0, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('H') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('H'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
|
@ -278,16 +278,16 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"c:\\foo", realArgs.TerminalArgs().StartingDirectory());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid0, guid);
|
||||
VERIFY_ARE_EQUAL(guid0, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(L"c:\\foo", termSettings.StartingDirectory());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('I') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('I'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
|
@ -301,16 +301,16 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_ARE_EQUAL(L"c:\\foo", realArgs.TerminalArgs().StartingDirectory());
|
||||
VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(profile2Guid, guid);
|
||||
VERIFY_ARE_EQUAL(profile2Guid, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(L"c:\\foo", termSettings.StartingDirectory());
|
||||
VERIFY_ARE_EQUAL(3, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('J') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('J'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
|
@ -323,16 +323,16 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"bar", realArgs.TerminalArgs().TabTitle());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid0, guid);
|
||||
VERIFY_ARE_EQUAL(guid0, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(L"bar", termSettings.StartingTitle());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('K') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('K'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
|
@ -346,16 +346,16 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_ARE_EQUAL(L"bar", realArgs.TerminalArgs().TabTitle());
|
||||
VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(profile2Guid, guid);
|
||||
VERIFY_ARE_EQUAL(profile2Guid, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(L"bar", termSettings.StartingTitle());
|
||||
VERIFY_ARE_EQUAL(3, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('L') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('L'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
|
@ -371,10 +371,10 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_ARE_EQUAL(L"bar", realArgs.TerminalArgs().TabTitle());
|
||||
VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid1, guid);
|
||||
VERIFY_ARE_EQUAL(guid1, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(L"bar", termSettings.StartingTitle());
|
||||
VERIFY_ARE_EQUAL(L"c:\\foo", termSettings.StartingDirectory());
|
||||
|
@ -382,9 +382,9 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
}
|
||||
|
||||
void TerminalSettingsTests::MakeSettingsForProfileThatDoesntExist()
|
||||
void TerminalSettingsTests::MakeSettingsForProfile()
|
||||
{
|
||||
// Test that making settings throws when the GUID doesn't exist
|
||||
// Test that making settings generally works.
|
||||
const std::string settingsString{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
|
@ -405,32 +405,32 @@ namespace SettingsModelLocalTests
|
|||
|
||||
const auto guid1 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
|
||||
const auto guid2 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
|
||||
const auto guid3 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-3333-49a3-80bd-e8fdd045185c}");
|
||||
|
||||
const auto profile1 = settings.FindProfile(guid1);
|
||||
const auto profile2 = settings.FindProfile(guid2);
|
||||
|
||||
try
|
||||
{
|
||||
auto terminalSettings{ TerminalSettings::CreateWithProfileByID(settings, guid1, nullptr) };
|
||||
auto terminalSettings{ TerminalSettings::CreateWithProfile(settings, profile1, nullptr) };
|
||||
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
|
||||
VERIFY_ARE_EQUAL(1, terminalSettings.DefaultSettings().HistorySize());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
VERIFY_IS_TRUE(false, L"This call to CreateWithProfileByID should succeed");
|
||||
VERIFY_IS_TRUE(false, L"This call to CreateWithProfile should succeed");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto terminalSettings{ TerminalSettings::CreateWithProfileByID(settings, guid2, nullptr) };
|
||||
auto terminalSettings{ TerminalSettings::CreateWithProfile(settings, profile2, nullptr) };
|
||||
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
|
||||
VERIFY_ARE_EQUAL(2, terminalSettings.DefaultSettings().HistorySize());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
VERIFY_IS_TRUE(false, L"This call to CreateWithProfileByID should succeed");
|
||||
VERIFY_IS_TRUE(false, L"This call to CreateWithProfile should succeed");
|
||||
}
|
||||
|
||||
VERIFY_THROWS(auto terminalSettings = TerminalSettings::CreateWithProfileByID(settings, guid3, nullptr), wil::ResultException, L"This call to constructor should fail");
|
||||
|
||||
try
|
||||
{
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, nullptr, nullptr) };
|
||||
|
|
|
@ -56,6 +56,7 @@ namespace TerminalAppLocalTests
|
|||
TEST_METHOD(ParseComboCommandlineIntoArgs);
|
||||
TEST_METHOD(ParseFocusTabArgs);
|
||||
TEST_METHOD(ParseMoveFocusArgs);
|
||||
TEST_METHOD(ParseSwapPaneArgs);
|
||||
TEST_METHOD(ParseArgumentsWithParsingTerminators);
|
||||
TEST_METHOD(ParseFocusPaneArgs);
|
||||
|
||||
|
@ -1207,6 +1208,119 @@ namespace TerminalAppLocalTests
|
|||
}
|
||||
}
|
||||
|
||||
void CommandlineTest::ParseSwapPaneArgs()
|
||||
{
|
||||
const wchar_t* subcommand = L"swap-pane";
|
||||
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", subcommand };
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Just the subcommand, without a direction, should fail."));
|
||||
|
||||
_buildCommandlinesExpectFailureHelper(appArgs, 1u, rawCommands);
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", subcommand, L"left" };
|
||||
_buildCommandlinesHelper(appArgs, 1u, rawCommands);
|
||||
|
||||
VERIFY_ARE_EQUAL(2u, appArgs._startupActions.size());
|
||||
|
||||
// The first action is going to always be a new-tab action
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, appArgs._startupActions.at(0).Action());
|
||||
|
||||
auto actionAndArgs = appArgs._startupActions.at(1);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SwapPane, actionAndArgs.Action());
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
auto myArgs = actionAndArgs.Args().try_as<SwapPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_ARE_EQUAL(FocusDirection::Left, myArgs.Direction());
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", subcommand, L"right" };
|
||||
_buildCommandlinesHelper(appArgs, 1u, rawCommands);
|
||||
|
||||
VERIFY_ARE_EQUAL(2u, appArgs._startupActions.size());
|
||||
|
||||
// The first action is going to always be a new-tab action
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, appArgs._startupActions.at(0).Action());
|
||||
|
||||
auto actionAndArgs = appArgs._startupActions.at(1);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SwapPane, actionAndArgs.Action());
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
auto myArgs = actionAndArgs.Args().try_as<SwapPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_ARE_EQUAL(FocusDirection::Right, myArgs.Direction());
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", subcommand, L"up" };
|
||||
_buildCommandlinesHelper(appArgs, 1u, rawCommands);
|
||||
|
||||
VERIFY_ARE_EQUAL(2u, appArgs._startupActions.size());
|
||||
|
||||
// The first action is going to always be a new-tab action
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, appArgs._startupActions.at(0).Action());
|
||||
|
||||
auto actionAndArgs = appArgs._startupActions.at(1);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SwapPane, actionAndArgs.Action());
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
auto myArgs = actionAndArgs.Args().try_as<SwapPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_ARE_EQUAL(FocusDirection::Up, myArgs.Direction());
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", subcommand, L"down" };
|
||||
_buildCommandlinesHelper(appArgs, 1u, rawCommands);
|
||||
|
||||
VERIFY_ARE_EQUAL(2u, appArgs._startupActions.size());
|
||||
|
||||
// The first action is going to always be a new-tab action
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, appArgs._startupActions.at(0).Action());
|
||||
|
||||
auto actionAndArgs = appArgs._startupActions.at(1);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SwapPane, actionAndArgs.Action());
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
auto myArgs = actionAndArgs.Args().try_as<SwapPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_ARE_EQUAL(FocusDirection::Down, myArgs.Direction());
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", subcommand, L"badDirection" };
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"move-pane with an invalid direction should fail."));
|
||||
_buildCommandlinesExpectFailureHelper(appArgs, 1u, rawCommands);
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", subcommand, L"left", L";", subcommand, L"right" };
|
||||
_buildCommandlinesHelper(appArgs, 2u, rawCommands);
|
||||
|
||||
VERIFY_ARE_EQUAL(3u, appArgs._startupActions.size());
|
||||
|
||||
// The first action is going to always be a new-tab action
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, appArgs._startupActions.at(0).Action());
|
||||
|
||||
auto actionAndArgs = appArgs._startupActions.at(1);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SwapPane, actionAndArgs.Action());
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
auto myArgs = actionAndArgs.Args().try_as<SwapPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_ARE_EQUAL(FocusDirection::Left, myArgs.Direction());
|
||||
|
||||
actionAndArgs = appArgs._startupActions.at(2);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SwapPane, actionAndArgs.Action());
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
myArgs = actionAndArgs.Args().try_as<SwapPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_ARE_EQUAL(FocusDirection::Right, myArgs.Direction());
|
||||
}
|
||||
}
|
||||
|
||||
void CommandlineTest::ParseFocusPaneArgs()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
|
|
|
@ -82,6 +82,8 @@ namespace TerminalAppLocalTests
|
|||
TEST_METHOD(MoveFocusFromZoomedPane);
|
||||
TEST_METHOD(CloseZoomedPane);
|
||||
|
||||
TEST_METHOD(SwapPanes);
|
||||
|
||||
TEST_METHOD(NextMRUTab);
|
||||
TEST_METHOD(VerifyCommandPaletteTabSwitcherOrder);
|
||||
|
||||
|
@ -93,6 +95,8 @@ namespace TerminalAppLocalTests
|
|||
TEST_METHOD(TestPreviewDismissScheme);
|
||||
TEST_METHOD(TestPreviewSchemeWhilePreviewing);
|
||||
|
||||
TEST_METHOD(TestClampSwitchToTab);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
return true;
|
||||
|
@ -426,7 +430,7 @@ namespace TerminalAppLocalTests
|
|||
Log::Comment(L"Duplicate the tab, and don't crash");
|
||||
result = RunOnUIThread([&page]() {
|
||||
page->_DuplicateFocusedTab();
|
||||
VERIFY_ARE_EQUAL(2u, page->_tabs.Size(), L"We should gracefully do nothing here - the profile no longer exists.");
|
||||
VERIFY_ARE_EQUAL(3u, page->_tabs.Size(), L"We should successfully duplicate a tab hosting a deleted profile.");
|
||||
});
|
||||
VERIFY_SUCCEEDED(result);
|
||||
}
|
||||
|
@ -526,9 +530,9 @@ namespace TerminalAppLocalTests
|
|||
|
||||
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
|
||||
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
VERIFY_ARE_EQUAL(2,
|
||||
VERIFY_ARE_EQUAL(3,
|
||||
tab->GetLeafPaneCount(),
|
||||
L"We should gracefully do nothing here - the profile no longer exists.");
|
||||
L"We should successfully duplicate a pane hosting a deleted profile.");
|
||||
});
|
||||
VERIFY_SUCCEEDED(result);
|
||||
|
||||
|
@ -817,6 +821,212 @@ namespace TerminalAppLocalTests
|
|||
VERIFY_SUCCEEDED(result);
|
||||
}
|
||||
|
||||
void TabTests::SwapPanes()
|
||||
{
|
||||
auto page = _commonSetup();
|
||||
|
||||
Log::Comment(L"Setup 4 panes.");
|
||||
// Create the following layout
|
||||
// -------------------
|
||||
// | 1 | 2 |
|
||||
// | | |
|
||||
// -------------------
|
||||
// | 3 | 4 |
|
||||
// | | |
|
||||
// -------------------
|
||||
uint32_t firstId = 0, secondId = 0, thirdId = 0, fourthId = 0;
|
||||
TestOnUIThread([&]() {
|
||||
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
|
||||
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
firstId = tab->_activePane->Id().value();
|
||||
// We start with 1 tab, split vertically to get
|
||||
// -------------------
|
||||
// | 1 | 2 |
|
||||
// | | |
|
||||
// -------------------
|
||||
page->_SplitPane(SplitState::Vertical, SplitType::Duplicate, 0.5f, nullptr);
|
||||
secondId = tab->_activePane->Id().value();
|
||||
});
|
||||
Sleep(250);
|
||||
TestOnUIThread([&]() {
|
||||
// After this the `2` pane is focused, go back to `1` being focused
|
||||
page->_MoveFocus(FocusDirection::Left);
|
||||
});
|
||||
Sleep(250);
|
||||
TestOnUIThread([&]() {
|
||||
// Split again to make the 3rd tab
|
||||
// -------------------
|
||||
// | 1 | |
|
||||
// | | |
|
||||
// ---------| 2 |
|
||||
// | 3 | |
|
||||
// | | |
|
||||
// -------------------
|
||||
page->_SplitPane(SplitState::Horizontal, SplitType::Duplicate, 0.5f, nullptr);
|
||||
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
// Split again to make the 3rd tab
|
||||
thirdId = tab->_activePane->Id().value();
|
||||
});
|
||||
Sleep(250);
|
||||
TestOnUIThread([&]() {
|
||||
// After this the `3` pane is focused, go back to `2` being focused
|
||||
page->_MoveFocus(FocusDirection::Right);
|
||||
});
|
||||
Sleep(250);
|
||||
TestOnUIThread([&]() {
|
||||
// Split to create the final pane
|
||||
// -------------------
|
||||
// | 1 | 2 |
|
||||
// | | |
|
||||
// -------------------
|
||||
// | 3 | 4 |
|
||||
// | | |
|
||||
// -------------------
|
||||
page->_SplitPane(SplitState::Horizontal, SplitType::Duplicate, 0.5f, nullptr);
|
||||
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
fourthId = tab->_activePane->Id().value();
|
||||
});
|
||||
|
||||
Sleep(250);
|
||||
TestOnUIThread([&]() {
|
||||
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
VERIFY_ARE_EQUAL(4, tab->GetLeafPaneCount());
|
||||
// just to be complete, make sure we actually have 4 different ids
|
||||
VERIFY_ARE_NOT_EQUAL(firstId, fourthId);
|
||||
VERIFY_ARE_NOT_EQUAL(secondId, fourthId);
|
||||
VERIFY_ARE_NOT_EQUAL(thirdId, fourthId);
|
||||
VERIFY_ARE_NOT_EQUAL(firstId, thirdId);
|
||||
VERIFY_ARE_NOT_EQUAL(secondId, thirdId);
|
||||
VERIFY_ARE_NOT_EQUAL(firstId, secondId);
|
||||
});
|
||||
|
||||
// Gratuitous use of sleep to make sure that the UI has updated properly
|
||||
// after each operation.
|
||||
Sleep(250);
|
||||
// Now try to move the pane through the tree
|
||||
Log::Comment(L"Move pane to the left. This should swap panes 3 and 4");
|
||||
// -------------------
|
||||
// | 1 | 2 |
|
||||
// | | |
|
||||
// -------------------
|
||||
// | 4 | 3 |
|
||||
// | | |
|
||||
// -------------------
|
||||
TestOnUIThread([&]() {
|
||||
// Set up action
|
||||
SwapPaneArgs args{ FocusDirection::Left };
|
||||
ActionEventArgs eventArgs{ args };
|
||||
|
||||
page->_HandleSwapPane(nullptr, eventArgs);
|
||||
});
|
||||
|
||||
Sleep(250);
|
||||
|
||||
TestOnUIThread([&]() {
|
||||
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
VERIFY_ARE_EQUAL(4, tab->GetLeafPaneCount());
|
||||
// Our currently focused pane should be `4`
|
||||
VERIFY_ARE_EQUAL(fourthId, tab->_activePane->Id().value());
|
||||
|
||||
// Inspect the tree to make sure we swapped
|
||||
VERIFY_ARE_EQUAL(fourthId, tab->_rootPane->_firstChild->_secondChild->Id().value());
|
||||
VERIFY_ARE_EQUAL(thirdId, tab->_rootPane->_secondChild->_secondChild->Id().value());
|
||||
});
|
||||
|
||||
Sleep(250);
|
||||
|
||||
Log::Comment(L"Move pane to up. This should swap panes 1 and 4");
|
||||
// -------------------
|
||||
// | 4 | 2 |
|
||||
// | | |
|
||||
// -------------------
|
||||
// | 1 | 3 |
|
||||
// | | |
|
||||
// -------------------
|
||||
TestOnUIThread([&]() {
|
||||
// Set up action
|
||||
SwapPaneArgs args{ FocusDirection::Up };
|
||||
ActionEventArgs eventArgs{ args };
|
||||
|
||||
page->_HandleSwapPane(nullptr, eventArgs);
|
||||
});
|
||||
|
||||
Sleep(250);
|
||||
|
||||
TestOnUIThread([&]() {
|
||||
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
VERIFY_ARE_EQUAL(4, tab->GetLeafPaneCount());
|
||||
// Our currently focused pane should be `4`
|
||||
VERIFY_ARE_EQUAL(fourthId, tab->_activePane->Id().value());
|
||||
|
||||
// Inspect the tree to make sure we swapped
|
||||
VERIFY_ARE_EQUAL(fourthId, tab->_rootPane->_firstChild->_firstChild->Id().value());
|
||||
VERIFY_ARE_EQUAL(firstId, tab->_rootPane->_firstChild->_secondChild->Id().value());
|
||||
});
|
||||
|
||||
Sleep(250);
|
||||
|
||||
Log::Comment(L"Move pane to the right. This should swap panes 2 and 4");
|
||||
// -------------------
|
||||
// | 2 | 4 |
|
||||
// | | |
|
||||
// -------------------
|
||||
// | 1 | 3 |
|
||||
// | | |
|
||||
// -------------------
|
||||
TestOnUIThread([&]() {
|
||||
// Set up action
|
||||
SwapPaneArgs args{ FocusDirection::Right };
|
||||
ActionEventArgs eventArgs{ args };
|
||||
|
||||
page->_HandleSwapPane(nullptr, eventArgs);
|
||||
});
|
||||
|
||||
Sleep(250);
|
||||
|
||||
TestOnUIThread([&]() {
|
||||
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
VERIFY_ARE_EQUAL(4, tab->GetLeafPaneCount());
|
||||
// Our currently focused pane should be `4`
|
||||
VERIFY_ARE_EQUAL(fourthId, tab->_activePane->Id().value());
|
||||
|
||||
// Inspect the tree to make sure we swapped
|
||||
VERIFY_ARE_EQUAL(fourthId, tab->_rootPane->_secondChild->_firstChild->Id().value());
|
||||
VERIFY_ARE_EQUAL(secondId, tab->_rootPane->_firstChild->_firstChild->Id().value());
|
||||
});
|
||||
|
||||
Sleep(250);
|
||||
|
||||
Log::Comment(L"Move pane down. This should swap panes 3 and 4");
|
||||
// -------------------
|
||||
// | 2 | 3 |
|
||||
// | | |
|
||||
// -------------------
|
||||
// | 1 | 4 |
|
||||
// | | |
|
||||
// -------------------
|
||||
TestOnUIThread([&]() {
|
||||
// Set up action
|
||||
SwapPaneArgs args{ FocusDirection::Down };
|
||||
ActionEventArgs eventArgs{ args };
|
||||
|
||||
page->_HandleSwapPane(nullptr, eventArgs);
|
||||
});
|
||||
|
||||
Sleep(250);
|
||||
|
||||
TestOnUIThread([&]() {
|
||||
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
VERIFY_ARE_EQUAL(4, tab->GetLeafPaneCount());
|
||||
// Our currently focused pane should be `4`
|
||||
VERIFY_ARE_EQUAL(fourthId, tab->_activePane->Id().value());
|
||||
|
||||
// Inspect the tree to make sure we swapped
|
||||
VERIFY_ARE_EQUAL(fourthId, tab->_rootPane->_secondChild->_secondChild->Id().value());
|
||||
VERIFY_ARE_EQUAL(thirdId, tab->_rootPane->_secondChild->_firstChild->Id().value());
|
||||
});
|
||||
}
|
||||
|
||||
void TabTests::NextMRUTab()
|
||||
{
|
||||
// This is a test for GH#8025 - we want to make sure that we can do both
|
||||
|
@ -1342,4 +1552,55 @@ namespace TerminalAppLocalTests
|
|||
});
|
||||
}
|
||||
|
||||
void TabTests::TestClampSwitchToTab()
|
||||
{
|
||||
Log::Comment(L"Test that switching to a tab index higher than the number of tabs just clamps to the last tab.");
|
||||
|
||||
auto page = _commonSetup();
|
||||
VERIFY_IS_NOT_NULL(page);
|
||||
|
||||
Log::Comment(L"Create a second tab");
|
||||
TestOnUIThread([&page]() {
|
||||
NewTerminalArgs newTerminalArgs{ 1 };
|
||||
page->_OpenNewTab(newTerminalArgs);
|
||||
});
|
||||
VERIFY_ARE_EQUAL(2u, page->_tabs.Size());
|
||||
|
||||
Log::Comment(L"Create a third tab");
|
||||
TestOnUIThread([&page]() {
|
||||
NewTerminalArgs newTerminalArgs{ 2 };
|
||||
page->_OpenNewTab(newTerminalArgs);
|
||||
});
|
||||
VERIFY_ARE_EQUAL(3u, page->_tabs.Size());
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
auto focusedTabIndexOpt{ page->_GetFocusedTabIndex() };
|
||||
VERIFY_IS_TRUE(focusedTabIndexOpt.has_value());
|
||||
VERIFY_ARE_EQUAL(2u, focusedTabIndexOpt.value());
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
Log::Comment(L"Switch to the first tab");
|
||||
page->_SelectTab(0);
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
auto focusedTabIndexOpt{ page->_GetFocusedTabIndex() };
|
||||
|
||||
VERIFY_IS_TRUE(focusedTabIndexOpt.has_value());
|
||||
VERIFY_ARE_EQUAL(0u, focusedTabIndexOpt.value());
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
Log::Comment(L"Switch to the tab 6, which is greater than number of tabs. This should switch to the third tab");
|
||||
page->_SelectTab(6);
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
auto focusedTabIndexOpt{ page->_GetFocusedTabIndex() };
|
||||
VERIFY_IS_TRUE(focusedTabIndexOpt.has_value());
|
||||
VERIFY_ARE_EQUAL(2u, focusedTabIndexOpt.value());
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -92,11 +92,11 @@
|
|||
<!-- From Microsoft.UI.Xaml.targets -->
|
||||
<Native-Platform Condition="'$(Platform)' == 'Win32'">x86</Native-Platform>
|
||||
<Native-Platform Condition="'$(Platform)' != 'Win32'">$(Platform)</Native-Platform>
|
||||
<_MUXBinRoot>"$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\runtimes\win10-$(Native-Platform)\native\"</_MUXBinRoot>
|
||||
<_MUXBinRoot>"$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\runtimes\win10-$(Native-Platform)\native\"</_MUXBinRoot>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- We actually can just straight up reference MUX here, it's fine -->
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" />
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -123,7 +123,7 @@
|
|||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Import Project="$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
|
||||
<Import Project="$(OpenConsoleDir)\src\common.build.post.props" />
|
||||
|
||||
|
|
|
@ -28,8 +28,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
CATCH_LOG();
|
||||
}
|
||||
|
||||
// This is a private constructor to be used in unit tests, where we don't
|
||||
// want each Monarch to necessarily use the current PID.
|
||||
// This constructor is intended to be used in unit tests,
|
||||
// but we need to make it public in order to use make_self
|
||||
// in the tests. It's not exposed through the idl though
|
||||
// so it's not _truly_ fully public which should be acceptable.
|
||||
Monarch::Monarch(const uint64_t testPID) :
|
||||
_ourPID{ testPID }
|
||||
{
|
||||
|
@ -78,6 +80,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
peasant.IdentifyWindowsRequested({ this, &Monarch::_identifyWindows });
|
||||
peasant.RenameRequested({ this, &Monarch::_renameRequested });
|
||||
|
||||
peasant.ShowTrayIconRequested([this](auto&&, auto&&) { _ShowTrayIconRequestedHandlers(*this, nullptr); });
|
||||
peasant.HideTrayIconRequested([this](auto&&, auto&&) { _HideTrayIconRequestedHandlers(*this, nullptr); });
|
||||
|
||||
_peasants[newPeasantsId] = peasant;
|
||||
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
|
@ -201,6 +206,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
_clearOldMruEntries(id);
|
||||
}
|
||||
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_lookupPeasantIdForName",
|
||||
TraceLoggingWideString(std::wstring{ name }.c_str(), "name", "the name we're looking for"),
|
||||
TraceLoggingUInt64(result, "peasantID", "the ID of the peasant with that name"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -732,24 +743,55 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
try
|
||||
{
|
||||
args.FoundMatch(false);
|
||||
|
||||
// If a WindowID is provided from the args, use that first.
|
||||
uint64_t windowId = 0;
|
||||
// If no name was provided, then just summon the MRU window.
|
||||
if (searchedForName.empty())
|
||||
if (args.WindowID())
|
||||
{
|
||||
// Use the value of the `desktop` arg to determine if we should
|
||||
// limit to the current desktop (desktop:onCurrent) or not
|
||||
// (desktop:any or desktop:toCurrent)
|
||||
windowId = _getMostRecentPeasantID(args.OnCurrentDesktop(), false);
|
||||
windowId = args.WindowID().Value();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to find a peasant that currently has this name
|
||||
windowId = _lookupPeasantIdForName(searchedForName);
|
||||
// If no name was provided, then just summon the MRU window.
|
||||
if (searchedForName.empty())
|
||||
{
|
||||
// Use the value of the `desktop` arg to determine if we should
|
||||
// limit to the current desktop (desktop:onCurrent) or not
|
||||
// (desktop:any or desktop:toCurrent)
|
||||
windowId = _getMostRecentPeasantID(args.OnCurrentDesktop(), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to find a peasant that currently has this name
|
||||
windowId = _lookupPeasantIdForName(searchedForName);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto targetPeasant{ _getPeasant(windowId) })
|
||||
{
|
||||
targetPeasant.Summon(args.SummonBehavior());
|
||||
args.FoundMatch(true);
|
||||
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_SummonWindow_Success",
|
||||
TraceLoggingWideString(searchedForName.c_str(), "searchedForName", "The name of the window we tried to summon"),
|
||||
TraceLoggingUInt64(windowId, "peasantID", "The id of the window we tried to summon"),
|
||||
TraceLoggingBoolean(args.OnCurrentDesktop(), "OnCurrentDesktop", "true iff the window needs to be on the current virtual desktop"),
|
||||
TraceLoggingBoolean(args.SummonBehavior().MoveToCurrentDesktop(), "MoveToCurrentDesktop", "if true, move the window to the current virtual desktop"),
|
||||
TraceLoggingBoolean(args.SummonBehavior().ToggleVisibility(), "ToggleVisibility", "true if we should toggle the visibility of the window"),
|
||||
TraceLoggingUInt32(args.SummonBehavior().DropdownDuration(), "DropdownDuration", "the duration to dropdown the window"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
else
|
||||
{
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_SummonWindow_NoPeasant",
|
||||
TraceLoggingWideString(searchedForName.c_str(), "searchedForName", "The name of the window we tried to summon"),
|
||||
TraceLoggingUInt64(windowId, "peasantID", "The id of the window we tried to summon"),
|
||||
TraceLoggingBoolean(args.OnCurrentDesktop(), "OnCurrentDesktop", "true iff the window needs to be on the current virtual desktop"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
|
@ -762,4 +804,56 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - This method creates a map of peasant IDs to peasant names
|
||||
// while removing dead peasants.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - A map of peasant IDs to their names.
|
||||
Windows::Foundation::Collections::IMapView<uint64_t, winrt::hstring> Monarch::GetPeasantNames()
|
||||
{
|
||||
auto names = winrt::single_threaded_map<uint64_t, winrt::hstring>();
|
||||
|
||||
std::vector<uint64_t> peasantsToErase{};
|
||||
for (const auto& [id, p] : _peasants)
|
||||
{
|
||||
try
|
||||
{
|
||||
names.Insert(id, p.WindowName());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
peasantsToErase.push_back(id);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the dead peasants we came across while iterating.
|
||||
for (const auto& id : peasantsToErase)
|
||||
{
|
||||
_peasants.erase(id);
|
||||
_clearOldMruEntries(id);
|
||||
}
|
||||
|
||||
return names.GetView();
|
||||
}
|
||||
|
||||
void Monarch::SummonAllWindows()
|
||||
{
|
||||
auto callback = [](auto&& p, auto&& /*id*/) {
|
||||
SummonWindowBehavior args{};
|
||||
args.ToggleVisibility(false);
|
||||
p.Summon(args);
|
||||
};
|
||||
auto onError = [](auto&& id) {
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_SummonAll_Failed",
|
||||
TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not summon"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
};
|
||||
_forAllPeasantsIgnoringTheDead(callback, onError);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
struct Monarch : public MonarchT<Monarch>
|
||||
{
|
||||
Monarch();
|
||||
Monarch(const uint64_t testPID);
|
||||
~Monarch();
|
||||
|
||||
uint64_t GetPID();
|
||||
|
@ -51,10 +52,14 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
void HandleActivatePeasant(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args);
|
||||
void SummonWindow(const Remoting::SummonWindowSelectionArgs& args);
|
||||
|
||||
void SummonAllWindows();
|
||||
Windows::Foundation::Collections::IMapView<uint64_t, winrt::hstring> GetPeasantNames();
|
||||
|
||||
TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs);
|
||||
TYPED_EVENT(ShowTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(HideTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
|
||||
private:
|
||||
Monarch(const uint64_t testPID);
|
||||
uint64_t _ourPID;
|
||||
|
||||
uint64_t _nextPeasantID{ 1 };
|
||||
|
|
|
@ -28,6 +28,7 @@ namespace Microsoft.Terminal.Remoting
|
|||
|
||||
Boolean FoundMatch;
|
||||
SummonWindowBehavior SummonBehavior;
|
||||
Windows.Foundation.IReference<UInt64> WindowID;
|
||||
}
|
||||
|
||||
|
||||
|
@ -40,6 +41,11 @@ namespace Microsoft.Terminal.Remoting
|
|||
void HandleActivatePeasant(WindowActivatedArgs args);
|
||||
void SummonWindow(SummonWindowSelectionArgs args);
|
||||
|
||||
void SummonAllWindows();
|
||||
Windows.Foundation.Collections.IMapView<UInt64, String> GetPeasantNames { get; };
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> ShowTrayIconRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> HideTrayIconRequested;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -20,8 +20,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
{
|
||||
}
|
||||
|
||||
// This is a private constructor to be used in unit tests, where we don't
|
||||
// want each Peasant to necessarily use the current PID.
|
||||
// This constructor is intended to be used in unit tests,
|
||||
// but we need to make it public in order to use make_self
|
||||
// in the tests. It's not exposed through the idl though
|
||||
// so it's not _truly_ fully public which should be acceptable.
|
||||
Peasant::Peasant(const uint64_t testPID) :
|
||||
_ourPID{ testPID }
|
||||
{
|
||||
|
@ -31,6 +33,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
{
|
||||
_id = id;
|
||||
}
|
||||
|
||||
uint64_t Peasant::GetID()
|
||||
{
|
||||
return _id;
|
||||
|
@ -222,4 +225,36 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
|
||||
void Peasant::RequestShowTrayIcon()
|
||||
{
|
||||
try
|
||||
{
|
||||
_ShowTrayIconRequestedHandlers(*this, nullptr);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
}
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Peasant_RequestShowTrayIcon",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
|
||||
void Peasant::RequestHideTrayIcon()
|
||||
{
|
||||
try
|
||||
{
|
||||
_HideTrayIconRequestedHandlers(*this, nullptr);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
}
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Peasant_RequestHideTrayIcon",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
void RequestIdentifyWindows();
|
||||
void DisplayWindowId();
|
||||
void RequestRename(const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args);
|
||||
void RequestShowTrayIcon();
|
||||
void RequestHideTrayIcon();
|
||||
|
||||
winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs GetLastActivatedArgs();
|
||||
|
||||
|
@ -40,6 +42,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
TYPED_EVENT(DisplayWindowIdRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(RenameRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::RenameRequestArgs);
|
||||
TYPED_EVENT(SummonRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior);
|
||||
TYPED_EVENT(ShowTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(HideTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
|
||||
private:
|
||||
Peasant(const uint64_t testPID);
|
||||
|
|
|
@ -64,6 +64,8 @@ namespace Microsoft.Terminal.Remoting
|
|||
void RequestIdentifyWindows(); // Tells us to raise a IdentifyWindowsRequested
|
||||
void RequestRename(RenameRequestArgs args); // Tells us to raise a RenameRequested
|
||||
void Summon(SummonWindowBehavior behavior);
|
||||
void RequestShowTrayIcon();
|
||||
void RequestHideTrayIcon();
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, WindowActivatedArgs> WindowActivated;
|
||||
event Windows.Foundation.TypedEventHandler<Object, CommandlineArgs> ExecuteCommandlineRequested;
|
||||
|
@ -71,6 +73,8 @@ namespace Microsoft.Terminal.Remoting
|
|||
event Windows.Foundation.TypedEventHandler<Object, Object> DisplayWindowIdRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, RenameRequestArgs> RenameRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, SummonWindowBehavior> SummonRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> ShowTrayIconRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> HideTrayIconRequested;
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass Peasant : IPeasant
|
||||
|
|
|
@ -34,6 +34,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
WINRT_PROPERTY(bool, FoundMatch, false);
|
||||
WINRT_PROPERTY(bool, OnCurrentDesktop, false);
|
||||
WINRT_PROPERTY(SummonWindowBehavior, SummonBehavior);
|
||||
|
||||
WINRT_PROPERTY(Windows::Foundation::IReference<uint64_t>, WindowID);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -254,6 +254,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
// window, and when the current monarch dies.
|
||||
|
||||
_monarch.FindTargetWindowRequested({ this, &WindowManager::_raiseFindTargetWindowRequested });
|
||||
_monarch.ShowTrayIconRequested([this](auto&&, auto&&) { _ShowTrayIconRequestedHandlers(*this, nullptr); });
|
||||
_monarch.HideTrayIconRequested([this](auto&&, auto&&) { _HideTrayIconRequestedHandlers(*this, nullptr); });
|
||||
|
||||
_BecameMonarchHandlers(*this, nullptr);
|
||||
}
|
||||
|
@ -509,4 +511,57 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
_monarch.SummonWindow(args);
|
||||
}
|
||||
|
||||
void WindowManager::SummonAllWindows()
|
||||
{
|
||||
if constexpr (Feature_TrayIcon::IsEnabled())
|
||||
{
|
||||
_monarch.SummonAllWindows();
|
||||
}
|
||||
}
|
||||
|
||||
Windows::Foundation::Collections::IMapView<uint64_t, winrt::hstring> WindowManager::GetPeasantNames()
|
||||
{
|
||||
// We should only get called when we're the monarch since the monarch
|
||||
// is the only one that knows about all peasants.
|
||||
return _monarch.GetPeasantNames();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Ask the monarch to show a tray icon.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
winrt::fire_and_forget WindowManager::RequestShowTrayIcon()
|
||||
{
|
||||
co_await winrt::resume_background();
|
||||
_peasant.RequestShowTrayIcon();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Ask the monarch to hide its tray icon.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
winrt::fire_and_forget WindowManager::RequestHideTrayIcon()
|
||||
{
|
||||
auto strongThis{ get_strong() };
|
||||
co_await winrt::resume_background();
|
||||
_peasant.RequestHideTrayIcon();
|
||||
}
|
||||
|
||||
bool WindowManager::DoesQuakeWindowExist()
|
||||
{
|
||||
const auto names = GetPeasantNames();
|
||||
for (const auto [id, name] : names)
|
||||
{
|
||||
if (name == QuakeWindowName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -40,8 +40,17 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
bool IsMonarch();
|
||||
void SummonWindow(const Remoting::SummonWindowSelectionArgs& args);
|
||||
|
||||
void SummonAllWindows();
|
||||
Windows::Foundation::Collections::IMapView<uint64_t, winrt::hstring> GetPeasantNames();
|
||||
|
||||
winrt::fire_and_forget RequestShowTrayIcon();
|
||||
winrt::fire_and_forget RequestHideTrayIcon();
|
||||
bool DoesQuakeWindowExist();
|
||||
|
||||
TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs);
|
||||
TYPED_EVENT(BecameMonarch, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(ShowTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(HideTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
|
||||
private:
|
||||
bool _shouldCreateWindow{ false };
|
||||
|
|
|
@ -12,7 +12,14 @@ namespace Microsoft.Terminal.Remoting
|
|||
IPeasant CurrentWindow();
|
||||
Boolean IsMonarch { get; };
|
||||
void SummonWindow(SummonWindowSelectionArgs args);
|
||||
void SummonAllWindows();
|
||||
void RequestShowTrayIcon();
|
||||
void RequestHideTrayIcon();
|
||||
Boolean DoesQuakeWindowExist();
|
||||
Windows.Foundation.Collections.IMapView<UInt64, String> GetPeasantNames();
|
||||
event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> BecameMonarch;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> ShowTrayIconRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> HideTrayIconRequested;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -20,7 +20,8 @@
|
|||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<!-- Include the MUX Controls resources -->
|
||||
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
|
||||
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls"
|
||||
ControlsResourcesVersion="Version1" />
|
||||
<ResourceDictionary>
|
||||
|
||||
<!--
|
||||
|
@ -55,12 +56,18 @@
|
|||
<!-- Define resources for Dark mode here -->
|
||||
<SolidColorBrush x:Key="TabViewBackground"
|
||||
Color="#FF333333" />
|
||||
|
||||
<SolidColorBrush x:Key="UnfocusedBorderBrush"
|
||||
Color="#FF333333" />
|
||||
</ResourceDictionary>
|
||||
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<!-- Define resources for Light mode here -->
|
||||
<SolidColorBrush x:Key="TabViewBackground"
|
||||
Color="#FFCCCCCC" />
|
||||
|
||||
<SolidColorBrush x:Key="UnfocusedBorderBrush"
|
||||
Color="#FFCCCCCC" />
|
||||
</ResourceDictionary>
|
||||
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
|
|
@ -143,6 +143,20 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleMovePane(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
if (args == nullptr)
|
||||
{
|
||||
args.Handled(false);
|
||||
}
|
||||
else if (const auto& realArgs = args.ActionArgs().try_as<MovePaneArgs>())
|
||||
{
|
||||
auto moved = _MovePane(realArgs.TabIndex());
|
||||
args.Handled(moved);
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleSplitPane(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
|
@ -161,6 +175,13 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleToggleSplitOrientation(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
_ToggleSplitOrientation();
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleTogglePaneZoom(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
|
@ -257,12 +278,12 @@ namespace winrt::TerminalApp::implementation
|
|||
{
|
||||
if (args == nullptr)
|
||||
{
|
||||
_OpenNewTab(nullptr);
|
||||
LOG_IF_FAILED(_OpenNewTab(nullptr));
|
||||
args.Handled(true);
|
||||
}
|
||||
else if (const auto& realArgs = args.ActionArgs().try_as<NewTabArgs>())
|
||||
{
|
||||
_OpenNewTab(realArgs.TerminalArgs());
|
||||
LOG_IF_FAILED(_OpenNewTab(realArgs.TerminalArgs()));
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
|
@ -307,8 +328,29 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
else
|
||||
{
|
||||
_MoveFocus(realArgs.FocusDirection());
|
||||
args.Handled(true);
|
||||
// Mark as handled only when the move succeeded (e.g. when there
|
||||
// is a pane to move to), otherwise mark as unhandled so the
|
||||
// keychord can propagate to the terminal (GH#6129)
|
||||
const auto moveSucceeded = _MoveFocus(realArgs.FocusDirection());
|
||||
args.Handled(moveSucceeded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleSwapPane(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<SwapPaneArgs>())
|
||||
{
|
||||
if (realArgs.Direction() == FocusDirection::None)
|
||||
{
|
||||
// Do nothing
|
||||
args.Handled(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto swapped = _SwapPane(realArgs.Direction());
|
||||
args.Handled(swapped);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -711,11 +753,10 @@ namespace winrt::TerminalApp::implementation
|
|||
newTerminalArgs = NewTerminalArgs();
|
||||
}
|
||||
|
||||
const auto profileGuid{ _settings.GetProfileForArgs(newTerminalArgs) };
|
||||
const auto settings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings) };
|
||||
const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) };
|
||||
|
||||
// Manually fill in the evaluated profile.
|
||||
newTerminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(profileGuid));
|
||||
newTerminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(profile.Guid()));
|
||||
_OpenNewWindow(false, newTerminalArgs);
|
||||
actionArgs.Handled(true);
|
||||
}
|
||||
|
|
|
@ -192,6 +192,8 @@ void AppCommandlineArgs::_buildParser()
|
|||
_buildSplitPaneParser();
|
||||
_buildFocusTabParser();
|
||||
_buildMoveFocusParser();
|
||||
_buildMovePaneParser();
|
||||
_buildSwapPaneParser();
|
||||
_buildFocusPaneParser();
|
||||
}
|
||||
|
||||
|
@ -296,6 +298,43 @@ void AppCommandlineArgs::_buildSplitPaneParser()
|
|||
setupSubcommand(_newPaneCommand);
|
||||
setupSubcommand(_newPaneShort);
|
||||
}
|
||||
// Method Description:
|
||||
// - Adds the `move-pane` subcommand and related options to the commandline parser.
|
||||
// - Additionally adds the `mp` subcommand, which is just a shortened version of `move-pane`
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void AppCommandlineArgs::_buildMovePaneParser()
|
||||
{
|
||||
_movePaneCommand = _app.add_subcommand("move-pane", RS_A(L"CmdMovePaneDesc"));
|
||||
_movePaneShort = _app.add_subcommand("mp", RS_A(L"CmdMPDesc"));
|
||||
|
||||
auto setupSubcommand = [this](auto* subcommand) {
|
||||
subcommand->add_option("-t,--tab",
|
||||
_movePaneTabIndex,
|
||||
RS_A(L"CmdMovePaneTabArgDesc"));
|
||||
|
||||
// When ParseCommand is called, if this subcommand was provided, this
|
||||
// callback function will be triggered on the same thread. We can be sure
|
||||
// that `this` will still be safe - this function just lets us know this
|
||||
// command was parsed.
|
||||
subcommand->callback([&, this]() {
|
||||
// Build the action from the values we've parsed on the commandline.
|
||||
ActionAndArgs movePaneAction{};
|
||||
|
||||
if (_movePaneTabIndex >= 0)
|
||||
{
|
||||
movePaneAction.Action(ShortcutAction::MovePane);
|
||||
MovePaneArgs args{ static_cast<unsigned int>(_movePaneTabIndex) };
|
||||
movePaneAction.Args(args);
|
||||
_startupActions.push_back(movePaneAction);
|
||||
}
|
||||
});
|
||||
};
|
||||
setupSubcommand(_movePaneCommand);
|
||||
setupSubcommand(_movePaneShort);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Adds the `focus-tab` subcommand and related options to the commandline parser.
|
||||
|
@ -341,6 +380,11 @@ void AppCommandlineArgs::_buildFocusTabParser()
|
|||
else if (_focusNextTab || _focusPrevTab)
|
||||
{
|
||||
focusTabAction.Action(_focusNextTab ? ShortcutAction::NextTab : ShortcutAction::PrevTab);
|
||||
// GH#10070 - make sure to not use the MRU order when switching
|
||||
// tabs on the commandline. That wouldn't make any sense!
|
||||
focusTabAction.Args(_focusNextTab ?
|
||||
static_cast<IActionArgs>(NextTabArgs(TabSwitcherMode::Disabled)) :
|
||||
static_cast<IActionArgs>(PrevTabArgs(TabSwitcherMode::Disabled)));
|
||||
_startupActions.push_back(std::move(focusTabAction));
|
||||
}
|
||||
});
|
||||
|
@ -350,6 +394,15 @@ void AppCommandlineArgs::_buildFocusTabParser()
|
|||
setupSubcommand(_focusTabShort);
|
||||
}
|
||||
|
||||
static const std::map<std::string, FocusDirection> focusDirectionMap = {
|
||||
{ "left", FocusDirection::Left },
|
||||
{ "right", FocusDirection::Right },
|
||||
{ "up", FocusDirection::Up },
|
||||
{ "down", FocusDirection::Down },
|
||||
{ "nextInOrder", FocusDirection::NextInOrder },
|
||||
{ "previousInOrder", FocusDirection::PreviousInOrder },
|
||||
};
|
||||
|
||||
// Method Description:
|
||||
// - Adds the `move-focus` subcommand and related options to the commandline parser.
|
||||
// - Additionally adds the `mf` subcommand, which is just a shortened version of `move-focus`
|
||||
|
@ -363,18 +416,11 @@ void AppCommandlineArgs::_buildMoveFocusParser()
|
|||
_moveFocusShort = _app.add_subcommand("mf", RS_A(L"CmdMFDesc"));
|
||||
|
||||
auto setupSubcommand = [this](auto* subcommand) {
|
||||
std::map<std::string, FocusDirection> map = {
|
||||
{ "left", FocusDirection::Left },
|
||||
{ "right", FocusDirection::Right },
|
||||
{ "up", FocusDirection::Up },
|
||||
{ "down", FocusDirection::Down }
|
||||
};
|
||||
|
||||
auto* directionOpt = subcommand->add_option("direction",
|
||||
_moveFocusDirection,
|
||||
RS_A(L"CmdMoveFocusDirectionArgDesc"));
|
||||
|
||||
directionOpt->transform(CLI::CheckedTransformer(map, CLI::ignore_case));
|
||||
directionOpt->transform(CLI::CheckedTransformer(focusDirectionMap, CLI::ignore_case));
|
||||
directionOpt->required();
|
||||
// When ParseCommand is called, if this subcommand was provided, this
|
||||
// callback function will be triggered on the same thread. We can be sure
|
||||
|
@ -398,6 +444,44 @@ void AppCommandlineArgs::_buildMoveFocusParser()
|
|||
setupSubcommand(_moveFocusShort);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Adds the `swap-pane` subcommand and related options to the commandline parser.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void AppCommandlineArgs::_buildSwapPaneParser()
|
||||
{
|
||||
_swapPaneCommand = _app.add_subcommand("swap-pane", RS_A(L"CmdSwapPaneDesc"));
|
||||
|
||||
auto setupSubcommand = [this](auto* subcommand) {
|
||||
auto* directionOpt = subcommand->add_option("direction",
|
||||
_swapPaneDirection,
|
||||
RS_A(L"CmdSwapPaneDirectionArgDesc"));
|
||||
|
||||
directionOpt->transform(CLI::CheckedTransformer(focusDirectionMap, CLI::ignore_case));
|
||||
directionOpt->required();
|
||||
// When ParseCommand is called, if this subcommand was provided, this
|
||||
// callback function will be triggered on the same thread. We can be sure
|
||||
// that `this` will still be safe - this function just lets us know this
|
||||
// command was parsed.
|
||||
subcommand->callback([&, this]() {
|
||||
if (_swapPaneDirection != FocusDirection::None)
|
||||
{
|
||||
SwapPaneArgs args{ _swapPaneDirection };
|
||||
|
||||
ActionAndArgs actionAndArgs{};
|
||||
actionAndArgs.Action(ShortcutAction::SwapPane);
|
||||
actionAndArgs.Args(args);
|
||||
|
||||
_startupActions.push_back(std::move(actionAndArgs));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
setupSubcommand(_swapPaneCommand);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Adds the `focus-pane` subcommand and related options to the commandline parser.
|
||||
// - Additionally adds the `fp` subcommand, which is just a shortened version of `focus-pane`
|
||||
|
@ -519,6 +603,13 @@ NewTerminalArgs AppCommandlineArgs::_getNewTerminalArgs(AppCommandlineArgs::NewT
|
|||
args.Profile(winrt::to_hstring(_profileName));
|
||||
}
|
||||
|
||||
if (!*subcommand.profileNameOption && !_commandline.empty())
|
||||
{
|
||||
// If there's no profile, but there IS a command line, set the tab title to the first part of the command
|
||||
// This will ensure that the tab we spawn has a name (since it didn't get one from its profile!)
|
||||
args.TabTitle(winrt::to_hstring(til::at(_commandline, 0)));
|
||||
}
|
||||
|
||||
if (*subcommand.startingDirectoryOption)
|
||||
{
|
||||
args.StartingDirectory(winrt::to_hstring(_startingDirectory));
|
||||
|
@ -574,6 +665,9 @@ bool AppCommandlineArgs::_noCommandsProvided()
|
|||
*_focusTabShort ||
|
||||
*_moveFocusCommand ||
|
||||
*_moveFocusShort ||
|
||||
*_movePaneCommand ||
|
||||
*_movePaneShort ||
|
||||
*_swapPaneCommand ||
|
||||
*_focusPaneCommand ||
|
||||
*_focusPaneShort ||
|
||||
*_newPaneShort.subcommand ||
|
||||
|
@ -602,11 +696,13 @@ void AppCommandlineArgs::_resetStateToDefault()
|
|||
_splitPaneSize = 0.5f;
|
||||
_splitDuplicate = false;
|
||||
|
||||
_movePaneTabIndex = -1;
|
||||
_focusTabIndex = -1;
|
||||
_focusNextTab = false;
|
||||
_focusPrevTab = false;
|
||||
|
||||
_moveFocusDirection = FocusDirection::None;
|
||||
_swapPaneDirection = FocusDirection::None;
|
||||
|
||||
_focusPaneTarget = -1;
|
||||
|
||||
|
|
|
@ -82,6 +82,9 @@ private:
|
|||
CLI::App* _focusTabShort;
|
||||
CLI::App* _moveFocusCommand;
|
||||
CLI::App* _moveFocusShort;
|
||||
CLI::App* _movePaneCommand;
|
||||
CLI::App* _movePaneShort;
|
||||
CLI::App* _swapPaneCommand;
|
||||
CLI::App* _focusPaneCommand;
|
||||
CLI::App* _focusPaneShort;
|
||||
|
||||
|
@ -95,6 +98,7 @@ private:
|
|||
bool _suppressApplicationTitle{ false };
|
||||
|
||||
winrt::Microsoft::Terminal::Settings::Model::FocusDirection _moveFocusDirection{ winrt::Microsoft::Terminal::Settings::Model::FocusDirection::None };
|
||||
winrt::Microsoft::Terminal::Settings::Model::FocusDirection _swapPaneDirection{ winrt::Microsoft::Terminal::Settings::Model::FocusDirection::None };
|
||||
|
||||
// _commandline will contain the command line with which we'll be spawning a new terminal
|
||||
std::vector<std::string> _commandline;
|
||||
|
@ -104,6 +108,7 @@ private:
|
|||
bool _splitDuplicate{ false };
|
||||
float _splitPaneSize{ 0.5f };
|
||||
|
||||
int _movePaneTabIndex{ -1 };
|
||||
int _focusTabIndex{ -1 };
|
||||
bool _focusNextTab{ false };
|
||||
bool _focusPrevTab{ false };
|
||||
|
@ -128,6 +133,8 @@ private:
|
|||
void _buildSplitPaneParser();
|
||||
void _buildFocusTabParser();
|
||||
void _buildMoveFocusParser();
|
||||
void _buildMovePaneParser();
|
||||
void _buildSwapPaneParser();
|
||||
void _buildFocusPaneParser();
|
||||
bool _noCommandsProvided();
|
||||
void _resetStateToDefault();
|
||||
|
|
|
@ -21,6 +21,11 @@ namespace winrt::TerminalApp::implementation
|
|||
return false;
|
||||
}
|
||||
|
||||
bool AppKeyBindings::IsKeyChordExplicitlyUnbound(const KeyChord& kc)
|
||||
{
|
||||
return _actionMap.IsKeyChordExplicitlyUnbound(kc);
|
||||
}
|
||||
|
||||
void AppKeyBindings::SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch)
|
||||
{
|
||||
_dispatch = dispatch;
|
||||
|
|
|
@ -20,6 +20,7 @@ namespace winrt::TerminalApp::implementation
|
|||
AppKeyBindings() = default;
|
||||
|
||||
bool TryKeyChord(winrt::Microsoft::Terminal::Control::KeyChord const& kc);
|
||||
bool IsKeyChordExplicitlyUnbound(winrt::Microsoft::Terminal::Control::KeyChord const& kc);
|
||||
|
||||
void SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch);
|
||||
void SetActionMap(const Microsoft::Terminal::Settings::Model::IActionMapView& actionMap);
|
||||
|
|
|
@ -203,12 +203,16 @@ namespace winrt::TerminalApp::implementation
|
|||
_isElevated = _isUserAdmin();
|
||||
_root = winrt::make_self<TerminalPage>();
|
||||
|
||||
_reloadSettings = std::make_shared<ThrottledFuncTrailing<>>(_root->Dispatcher(), std::chrono::milliseconds(100), [weakSelf = get_weak()]() {
|
||||
_reloadSettings = std::make_shared<ThrottledFuncTrailing<>>(winrt::Windows::System::DispatcherQueue::GetForCurrentThread(), std::chrono::milliseconds(100), [weakSelf = get_weak()]() {
|
||||
if (auto self{ weakSelf.get() })
|
||||
{
|
||||
self->_ReloadSettings();
|
||||
}
|
||||
});
|
||||
|
||||
_languageProfileNotifier = winrt::make_self<LanguageProfileNotifier>([this]() {
|
||||
_reloadSettings->Run();
|
||||
});
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -1125,28 +1129,11 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Gets the taskbar state value from the last active control
|
||||
// Return Value:
|
||||
// - The taskbar state of the last active control
|
||||
size_t AppLogic::GetLastActiveControlTaskbarState()
|
||||
winrt::TerminalApp::TaskbarState AppLogic::TaskbarState()
|
||||
{
|
||||
if (_root)
|
||||
{
|
||||
return _root->GetLastActiveControlTaskbarState();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Gets the taskbar progress value from the last active control
|
||||
// Return Value:
|
||||
// - The taskbar progress of the last active control
|
||||
size_t AppLogic::GetLastActiveControlTaskbarProgress()
|
||||
{
|
||||
if (_root)
|
||||
{
|
||||
return _root->GetLastActiveControlTaskbarProgress();
|
||||
return _root->TaskbarState();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
@ -1229,6 +1216,11 @@ namespace winrt::TerminalApp::implementation
|
|||
auto actions = winrt::single_threaded_vector<ActionAndArgs>(std::move(appArgs.GetStartupActions()));
|
||||
|
||||
_root->ProcessStartupActions(actions, false, cwd);
|
||||
|
||||
if (appArgs.IsHandoffListener())
|
||||
{
|
||||
_root->SetInboundListener(true);
|
||||
}
|
||||
}
|
||||
// Return the result of parsing with commandline, though it may or may not be used.
|
||||
return result;
|
||||
|
@ -1450,4 +1442,39 @@ namespace winrt::TerminalApp::implementation
|
|||
return _root->IsQuakeWindow();
|
||||
}
|
||||
|
||||
bool AppLogic::GetMinimizeToTray()
|
||||
{
|
||||
if constexpr (Feature_TrayIcon::IsEnabled())
|
||||
{
|
||||
if (!_loadedInitialSettings)
|
||||
{
|
||||
// Load settings if we haven't already
|
||||
LoadSettings();
|
||||
}
|
||||
|
||||
return _settings.GlobalSettings().MinimizeToTray();
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool AppLogic::GetAlwaysShowTrayIcon()
|
||||
{
|
||||
if constexpr (Feature_TrayIcon::IsEnabled())
|
||||
{
|
||||
if (!_loadedInitialSettings)
|
||||
{
|
||||
// Load settings if we haven't already
|
||||
LoadSettings();
|
||||
}
|
||||
|
||||
return _settings.GlobalSettings().AlwaysShowTrayIcon();
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,8 +5,9 @@
|
|||
|
||||
#include "AppLogic.g.h"
|
||||
#include "FindTargetWindowResult.g.h"
|
||||
#include "TerminalPage.h"
|
||||
#include "Jumplist.h"
|
||||
#include "LanguageProfileNotifier.h"
|
||||
#include "TerminalPage.h"
|
||||
|
||||
#include <inc/cppwinrt_utils.h>
|
||||
#include <ThrottledFunc.h>
|
||||
|
@ -89,8 +90,10 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
void WindowCloseButtonClicked();
|
||||
|
||||
size_t GetLastActiveControlTaskbarState();
|
||||
size_t GetLastActiveControlTaskbarProgress();
|
||||
winrt::TerminalApp::TaskbarState TaskbarState();
|
||||
|
||||
bool GetMinimizeToTray();
|
||||
bool GetAlwaysShowTrayIcon();
|
||||
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog);
|
||||
|
||||
|
@ -110,12 +113,8 @@ 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 };
|
||||
|
||||
Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr };
|
||||
|
||||
wil::unique_folder_change_reader_nothrow _reader;
|
||||
std::shared_ptr<ThrottledFuncTrailing<>> _reloadSettings;
|
||||
til::throttled_func_trailing<> _reloadState;
|
||||
winrt::hstring _settingsLoadExceptionText;
|
||||
HRESULT _settingsLoadedResult = S_OK;
|
||||
bool _loadedInitialSettings = false;
|
||||
|
@ -124,6 +123,15 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
::TerminalApp::AppCommandlineArgs _appArgs;
|
||||
::TerminalApp::AppCommandlineArgs _settingsAppArgs;
|
||||
|
||||
std::shared_ptr<ThrottledFuncTrailing<>> _reloadSettings;
|
||||
til::throttled_func_trailing<> _reloadState;
|
||||
|
||||
// These fields invoke _reloadSettings and must be destroyed before _reloadSettings.
|
||||
// (C++ destroys members in reverse-declaration-order.)
|
||||
winrt::com_ptr<LanguageProfileNotifier> _languageProfileNotifier;
|
||||
wil::unique_folder_change_reader_nothrow _reader;
|
||||
|
||||
static TerminalApp::FindTargetWindowResult _doFindTargetWindow(winrt::array_view<const hstring> args,
|
||||
const Microsoft::Terminal::Settings::Model::WindowingMode& windowingBehavior);
|
||||
|
||||
|
|
|
@ -68,8 +68,10 @@ namespace TerminalApp
|
|||
void TitlebarClicked();
|
||||
void WindowCloseButtonClicked();
|
||||
|
||||
UInt64 GetLastActiveControlTaskbarState();
|
||||
UInt64 GetLastActiveControlTaskbarProgress();
|
||||
TaskbarState TaskbarState{ get; };
|
||||
|
||||
Boolean GetMinimizeToTray();
|
||||
Boolean GetAlwaysShowTrayIcon();
|
||||
|
||||
FindTargetWindowResult FindTargetWindow(String[] args);
|
||||
|
||||
|
|
|
@ -250,10 +250,12 @@ namespace winrt::TerminalApp::implementation
|
|||
void CommandPalette::_previewKeyDownHandler(IInspectable const& /*sender*/,
|
||||
Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e)
|
||||
{
|
||||
auto key = e.OriginalKey();
|
||||
auto const ctrlDown = WI_IsFlagSet(CoreWindow::GetForCurrentThread().GetKeyState(winrt::Windows::System::VirtualKey::Control), CoreVirtualKeyStates::Down);
|
||||
auto const altDown = WI_IsFlagSet(CoreWindow::GetForCurrentThread().GetKeyState(winrt::Windows::System::VirtualKey::Menu), CoreVirtualKeyStates::Down);
|
||||
auto const shiftDown = WI_IsFlagSet(CoreWindow::GetForCurrentThread().GetKeyState(winrt::Windows::System::VirtualKey::Shift), CoreVirtualKeyStates::Down);
|
||||
const auto key = e.OriginalKey();
|
||||
const auto scanCode = e.KeyStatus().ScanCode;
|
||||
const auto coreWindow = CoreWindow::GetForCurrentThread();
|
||||
const auto ctrlDown = WI_IsFlagSet(coreWindow.GetKeyState(winrt::Windows::System::VirtualKey::Control), CoreVirtualKeyStates::Down);
|
||||
const auto altDown = WI_IsFlagSet(coreWindow.GetKeyState(winrt::Windows::System::VirtualKey::Menu), CoreVirtualKeyStates::Down);
|
||||
const auto shiftDown = WI_IsFlagSet(coreWindow.GetKeyState(winrt::Windows::System::VirtualKey::Shift), CoreVirtualKeyStates::Down);
|
||||
|
||||
// Some keypresses such as Tab, Return, Esc, and Arrow Keys are ignored by controls because
|
||||
// they're not considered input key presses. While they don't raise KeyDown events,
|
||||
|
@ -264,7 +266,7 @@ namespace winrt::TerminalApp::implementation
|
|||
// a really widely used keyboard navigation key.
|
||||
if (_currentMode == CommandPaletteMode::TabSwitchMode && _actionMap)
|
||||
{
|
||||
winrt::Microsoft::Terminal::Control::KeyChord kc{ ctrlDown, altDown, shiftDown, static_cast<int32_t>(key) };
|
||||
winrt::Microsoft::Terminal::Control::KeyChord kc{ ctrlDown, altDown, shiftDown, false, static_cast<int32_t>(key), static_cast<int32_t>(scanCode) };
|
||||
if (const auto cmd{ _actionMap.GetActionByKeyChord(kc) })
|
||||
{
|
||||
if (cmd.ActionAndArgs().Action() == ShortcutAction::PrevTab)
|
||||
|
@ -402,9 +404,10 @@ namespace winrt::TerminalApp::implementation
|
|||
// - <none>
|
||||
void CommandPalette::_anchorKeyUpHandler()
|
||||
{
|
||||
auto const ctrlDown = WI_IsFlagSet(CoreWindow::GetForCurrentThread().GetKeyState(winrt::Windows::System::VirtualKey::Control), CoreVirtualKeyStates::Down);
|
||||
auto const altDown = WI_IsFlagSet(CoreWindow::GetForCurrentThread().GetKeyState(winrt::Windows::System::VirtualKey::Menu), CoreVirtualKeyStates::Down);
|
||||
auto const shiftDown = WI_IsFlagSet(CoreWindow::GetForCurrentThread().GetKeyState(winrt::Windows::System::VirtualKey::Shift), CoreVirtualKeyStates::Down);
|
||||
const auto coreWindow = CoreWindow::GetForCurrentThread();
|
||||
const auto ctrlDown = WI_IsFlagSet(coreWindow.GetKeyState(winrt::Windows::System::VirtualKey::Control), CoreVirtualKeyStates::Down);
|
||||
const auto altDown = WI_IsFlagSet(coreWindow.GetKeyState(winrt::Windows::System::VirtualKey::Menu), CoreVirtualKeyStates::Down);
|
||||
const auto shiftDown = WI_IsFlagSet(coreWindow.GetKeyState(winrt::Windows::System::VirtualKey::Shift), CoreVirtualKeyStates::Down);
|
||||
|
||||
if (!ctrlDown && !altDown && !shiftDown)
|
||||
{
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace winrt::Microsoft::TerminalApp::implementation
|
|||
_wrappedConnection{ std::move(wrappedConnection) }
|
||||
{
|
||||
}
|
||||
void Initialize(const Windows::Foundation::Collections::ValueSet& /*settings*/) {}
|
||||
~DebugInputTapConnection() = default;
|
||||
void Start()
|
||||
{
|
||||
|
|
|
@ -13,6 +13,7 @@ namespace winrt::Microsoft::TerminalApp::implementation
|
|||
{
|
||||
public:
|
||||
explicit DebugTapConnection(Microsoft::Terminal::TerminalConnection::ITerminalConnection wrappedConnection);
|
||||
void Initialize(const Windows::Foundation::Collections::ValueSet& /*settings*/){};
|
||||
~DebugTapConnection();
|
||||
void Start();
|
||||
void WriteInput(hstring const& data);
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "LanguageProfileNotifier.h"
|
||||
|
||||
using namespace winrt::TerminalApp::implementation;
|
||||
|
||||
LanguageProfileNotifier::LanguageProfileNotifier(std::function<void()>&& callback) :
|
||||
_callback{ std::move(callback) },
|
||||
_currentKeyboardLayout{ GetKeyboardLayout(0) }
|
||||
{
|
||||
const auto manager = wil::CoCreateInstance<ITfThreadMgr>(CLSID_TF_ThreadMgr);
|
||||
_source = manager.query<ITfSource>();
|
||||
if (FAILED(_source->AdviseSink(IID_ITfInputProcessorProfileActivationSink, static_cast<ITfInputProcessorProfileActivationSink*>(this), &_cookie)))
|
||||
{
|
||||
_cookie = TF_INVALID_COOKIE;
|
||||
THROW_LAST_ERROR();
|
||||
}
|
||||
}
|
||||
|
||||
LanguageProfileNotifier::~LanguageProfileNotifier()
|
||||
{
|
||||
if (_cookie != TF_INVALID_COOKIE)
|
||||
{
|
||||
_source->UnadviseSink(_cookie);
|
||||
}
|
||||
}
|
||||
|
||||
STDMETHODIMP LanguageProfileNotifier::OnActivated(DWORD /*dwProfileType*/, LANGID /*langid*/, REFCLSID /*clsid*/, REFGUID /*catid*/, REFGUID /*guidProfile*/, HKL hkl, DWORD /*dwFlags*/)
|
||||
{
|
||||
if (hkl && hkl != _currentKeyboardLayout)
|
||||
{
|
||||
_currentKeyboardLayout = hkl;
|
||||
try
|
||||
{
|
||||
_callback();
|
||||
}
|
||||
CATCH_RETURN();
|
||||
}
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
class LanguageProfileNotifier : public winrt::implements<LanguageProfileNotifier, ITfInputProcessorProfileActivationSink>
|
||||
{
|
||||
public:
|
||||
explicit LanguageProfileNotifier(std::function<void()>&& callback);
|
||||
~LanguageProfileNotifier();
|
||||
STDMETHODIMP OnActivated(DWORD dwProfileType, LANGID langid, REFCLSID clsid, REFGUID catid, REFGUID guidProfile, HKL hkl, DWORD dwFlags);
|
||||
|
||||
private:
|
||||
std::function<void()> _callback;
|
||||
wil::com_ptr<ITfSource> _source;
|
||||
DWORD _cookie = TF_INVALID_COOKIE;
|
||||
HKL _currentKeyboardLayout;
|
||||
};
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -21,6 +21,18 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../cascadia/inc/cppwinrt_utils.h"
|
||||
#include "TaskbarState.h"
|
||||
|
||||
// fwdecl unittest classes
|
||||
namespace TerminalAppLocalTests
|
||||
{
|
||||
class TabTests;
|
||||
};
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
struct TerminalTab;
|
||||
}
|
||||
|
||||
enum class Borders : int
|
||||
{
|
||||
|
@ -35,13 +47,21 @@ DEFINE_ENUM_FLAG_OPERATORS(Borders);
|
|||
class Pane : public std::enable_shared_from_this<Pane>
|
||||
{
|
||||
public:
|
||||
Pane(const GUID& profile,
|
||||
Pane(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile,
|
||||
const winrt::Microsoft::Terminal::Control::TermControl& control,
|
||||
const bool lastFocused = false);
|
||||
|
||||
std::shared_ptr<Pane> GetActivePane();
|
||||
winrt::Microsoft::Terminal::Control::TermControl GetTerminalControl();
|
||||
std::optional<GUID> GetFocusedProfile();
|
||||
winrt::Microsoft::Terminal::Settings::Model::Profile GetFocusedProfile();
|
||||
|
||||
// Method Description:
|
||||
// - If this is a leaf pane, return its profile.
|
||||
// - If this is a branch/root pane, return nullptr.
|
||||
winrt::Microsoft::Terminal::Settings::Model::Profile GetProfile() const
|
||||
{
|
||||
return _profile;
|
||||
}
|
||||
|
||||
winrt::Windows::UI::Xaml::Controls::Grid GetRootElement();
|
||||
|
||||
|
@ -51,16 +71,21 @@ public:
|
|||
void SetActive();
|
||||
|
||||
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings,
|
||||
const GUID& profile);
|
||||
const winrt::Microsoft::Terminal::Settings::Model::Profile& profile);
|
||||
void ResizeContent(const winrt::Windows::Foundation::Size& newSize);
|
||||
void Relayout();
|
||||
bool ResizePane(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction);
|
||||
bool NavigateFocus(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
std::shared_ptr<Pane> NavigateDirection(const std::shared_ptr<Pane> sourcePane, const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
bool SwapPanes(std::shared_ptr<Pane> first, std::shared_ptr<Pane> second);
|
||||
|
||||
std::shared_ptr<Pane> NextPane(const std::shared_ptr<Pane> pane);
|
||||
std::shared_ptr<Pane> PreviousPane(const std::shared_ptr<Pane> pane);
|
||||
|
||||
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Split(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType,
|
||||
const float splitSize,
|
||||
const GUID& profile,
|
||||
const winrt::Microsoft::Terminal::Settings::Model::Profile& profile,
|
||||
const winrt::Microsoft::Terminal::Control::TermControl& control);
|
||||
bool ToggleSplitOrientation();
|
||||
float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
|
||||
std::optional<winrt::Microsoft::Terminal::Settings::Model::SplitState> PreCalculateAutoSplit(const std::shared_ptr<Pane> target,
|
||||
const winrt::Windows::Foundation::Size parentSize) const;
|
||||
|
@ -71,6 +96,10 @@ public:
|
|||
void Shutdown();
|
||||
void Close();
|
||||
|
||||
std::shared_ptr<Pane> AttachPane(std::shared_ptr<Pane> pane,
|
||||
winrt::Microsoft::Terminal::Settings::Model::SplitState splitType);
|
||||
std::shared_ptr<Pane> DetachPane(std::shared_ptr<Pane> pane);
|
||||
|
||||
int GetLeafPaneCount() const noexcept;
|
||||
|
||||
void Maximize(std::shared_ptr<Pane> zoomedPane);
|
||||
|
@ -79,15 +108,47 @@ public:
|
|||
std::optional<uint32_t> Id() noexcept;
|
||||
void Id(uint32_t id) noexcept;
|
||||
bool FocusPane(const uint32_t id);
|
||||
bool FocusPane(const std::shared_ptr<Pane> pane);
|
||||
std::shared_ptr<Pane> FindPane(const uint32_t id);
|
||||
|
||||
bool ContainsReadOnly() const;
|
||||
|
||||
// Method Description:
|
||||
// - A helper method for ad-hoc recursion on a pane tree. Walks the pane
|
||||
// tree, calling a predicate on each pane in a depth-first pattern.
|
||||
// - If the predicate returns true, recursion is stopped early.
|
||||
// Arguments:
|
||||
// - f: The function to be applied to each pane.
|
||||
// Return Value:
|
||||
// - true if the predicate returned true on any pane.
|
||||
template<typename F>
|
||||
//requires std::predicate<F, std::shared_ptr<Pane>>
|
||||
bool WalkTree(F f)
|
||||
{
|
||||
if (f(shared_from_this()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!_IsLeaf())
|
||||
{
|
||||
return _firstChild->WalkTree(f) || _secondChild->WalkTree(f);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CollectTaskbarStates(std::vector<winrt::TerminalApp::TaskbarState>& states);
|
||||
|
||||
WINRT_CALLBACK(Closed, winrt::Windows::Foundation::EventHandler<winrt::Windows::Foundation::IInspectable>);
|
||||
DECLARE_EVENT(GotFocus, _GotFocusHandlers, winrt::delegate<std::shared_ptr<Pane>>);
|
||||
DECLARE_EVENT(LostFocus, _LostFocusHandlers, winrt::delegate<std::shared_ptr<Pane>>);
|
||||
DECLARE_EVENT(PaneRaiseBell, _PaneRaiseBellHandlers, winrt::Windows::Foundation::EventHandler<bool>);
|
||||
DECLARE_EVENT(Detached, _PaneDetachedHandlers, winrt::delegate<std::shared_ptr<Pane>>);
|
||||
|
||||
private:
|
||||
struct PanePoint;
|
||||
struct PaneNeighborSearch;
|
||||
struct SnapSizeResult;
|
||||
struct SnapChildrenSizeResult;
|
||||
struct LayoutSizeNode;
|
||||
|
@ -107,7 +168,7 @@ private:
|
|||
std::optional<uint32_t> _id;
|
||||
|
||||
bool _lastActive{ false };
|
||||
std::optional<GUID> _profile{ std::nullopt };
|
||||
winrt::Microsoft::Terminal::Settings::Model::Profile _profile{ nullptr };
|
||||
winrt::event_token _connectionStateChangedToken{ 0 };
|
||||
winrt::event_token _firstClosedToken{ 0 };
|
||||
winrt::event_token _secondClosedToken{ 0 };
|
||||
|
@ -128,16 +189,25 @@ private:
|
|||
|
||||
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> _Split(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType,
|
||||
const float splitSize,
|
||||
const GUID& profile,
|
||||
const winrt::Microsoft::Terminal::Control::TermControl& control);
|
||||
std::shared_ptr<Pane> newPane);
|
||||
|
||||
void _CreateRowColDefinitions();
|
||||
void _ApplySplitDefinitions();
|
||||
void _SetupEntranceAnimation();
|
||||
void _UpdateBorders();
|
||||
Borders _GetCommonBorders();
|
||||
|
||||
bool _Resize(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction);
|
||||
bool _NavigateFocus(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
|
||||
std::shared_ptr<Pane> _FindParentOfPane(const std::shared_ptr<Pane> pane);
|
||||
bool _IsAdjacent(const std::shared_ptr<Pane> first, const PanePoint firstOffset, const std::shared_ptr<Pane> second, const PanePoint secondOffset, const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction) const;
|
||||
PaneNeighborSearch _FindNeighborForPane(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction,
|
||||
PaneNeighborSearch searchResult,
|
||||
const bool focusIsSecondSide,
|
||||
const PanePoint offset);
|
||||
PaneNeighborSearch _FindPaneAndNeighbor(const std::shared_ptr<Pane> sourcePane,
|
||||
const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction,
|
||||
const PanePoint offset);
|
||||
|
||||
void _CloseChild(const bool closeFirst);
|
||||
winrt::fire_and_forget _CloseChildRoutine(const bool closeFirst);
|
||||
|
@ -200,6 +270,19 @@ private:
|
|||
|
||||
static void _SetupResources();
|
||||
|
||||
struct PanePoint
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
struct PaneNeighborSearch
|
||||
{
|
||||
std::shared_ptr<Pane> source;
|
||||
std::shared_ptr<Pane> neighbor;
|
||||
PanePoint sourceOffset;
|
||||
};
|
||||
|
||||
struct SnapSizeResult
|
||||
{
|
||||
float lower;
|
||||
|
@ -236,4 +319,7 @@ private:
|
|||
private:
|
||||
void _AssignChildNode(std::unique_ptr<LayoutSizeNode>& nodeField, const LayoutSizeNode* const newNode);
|
||||
};
|
||||
|
||||
friend struct winrt::TerminalApp::implementation::TerminalTab;
|
||||
friend class ::TerminalAppLocalTests::TabTests;
|
||||
};
|
||||
|
|
|
@ -282,6 +282,16 @@
|
|||
<data name="CmdFocusTabTargetArgDesc" xml:space="preserve">
|
||||
<value>Move focus the tab at the given index</value>
|
||||
</data>
|
||||
<data name="CmdMovePaneTabArgDesc" xml:space="preserve">
|
||||
<value>Move focused pane to the tab at the given index</value>
|
||||
</data>
|
||||
<data name="CmdMovePaneDesc" xml:space="preserve">
|
||||
<value>Move focused pane to another tab</value>
|
||||
</data>
|
||||
<data name="CmdMPDesc" xml:space="preserve">
|
||||
<value>An alias for the "move-pane" subcommand.</value>
|
||||
<comment>{Locked="\"move-pane\""}</comment>
|
||||
</data>
|
||||
<data name="CmdSplitPaneSizeArgDesc" xml:space="preserve">
|
||||
<value>Specify the size as a percentage of the parent pane. Valid values are between (0,1), exclusive.</value>
|
||||
</data>
|
||||
|
@ -359,6 +369,12 @@
|
|||
<data name="CmdMoveFocusDirectionArgDesc" xml:space="preserve">
|
||||
<value>The direction to move focus in</value>
|
||||
</data>
|
||||
<data name="CmdSwapPaneDesc" xml:space="preserve">
|
||||
<value>Swap the focused pane with the adjacent pane in the specified direction</value>
|
||||
</data>
|
||||
<data name="CmdSwapPaneDirectionArgDesc" xml:space="preserve">
|
||||
<value>The direction to move the focused pane in</value>
|
||||
</data>
|
||||
<data name="CmdFocusDesc" xml:space="preserve">
|
||||
<value>Launch the window in focus mode</value>
|
||||
</data>
|
||||
|
@ -637,6 +653,14 @@
|
|||
<data name="CommandPaletteMenuItem" xml:space="preserve">
|
||||
<value>Command Palette</value>
|
||||
</data>
|
||||
<data name="TrayIconFocusTerminal" xml:space="preserve">
|
||||
<value>Focus Terminal</value>
|
||||
<comment>This is displayed as a label for the context menu item that focuses the terminal.</comment>
|
||||
</data>
|
||||
<data name="TrayIconWindowSubmenu" xml:space="preserve">
|
||||
<value>Windows</value>
|
||||
<comment>This is displayed as a label for the context menu item that holds the submenu of available windows.</comment>
|
||||
</data>
|
||||
<data name="ShellExtension_OpenInTerminalMenuItem_Dev" xml:space="preserve">
|
||||
<value>Open in Windows Terminal (Dev)</value>
|
||||
<comment>{Locked} The dev build will never be seen in multiple languages</comment>
|
||||
|
@ -649,4 +673,16 @@
|
|||
<value>Open in Windows Terminal</value>
|
||||
<comment>{Locked="Windows"} This is a menu item that will be displayed in the Windows File Explorer that launches the non-preview version of Windows Terminal</comment>
|
||||
</data>
|
||||
<data name="DropPathTabRun.Text" xml:space="preserve">
|
||||
<value>Open a new tab in given starting directory</value>
|
||||
</data>
|
||||
<data name="SplitTabText" xml:space="preserve">
|
||||
<value>Split Tab</value>
|
||||
</data>
|
||||
<data name="DropPathTabNewWindow.Text" xml:space="preserve">
|
||||
<value>Open a new window with given starting directory</value>
|
||||
</data>
|
||||
<data name="DropPathTabSplit.Text" xml:space="preserve">
|
||||
<value>Split the window and start in given directory</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
|
@ -56,13 +56,13 @@ namespace winrt::TerminalApp::implementation
|
|||
// - existingConnection: An optional connection that is already established to a PTY
|
||||
// for this tab to host instead of creating one.
|
||||
// If not defined, the tab will create the connection.
|
||||
void TerminalPage::_OpenNewTab(const NewTerminalArgs& newTerminalArgs, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection)
|
||||
HRESULT TerminalPage::_OpenNewTab(const NewTerminalArgs& newTerminalArgs, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection)
|
||||
try
|
||||
{
|
||||
const auto profileGuid{ _settings.GetProfileForArgs(newTerminalArgs) };
|
||||
const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) };
|
||||
const auto settings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings) };
|
||||
|
||||
_CreateNewTabFromSettings(profileGuid, settings, existingConnection);
|
||||
_CreateNewTabWithProfileAndSettings(profile, settings, existingConnection);
|
||||
|
||||
const uint32_t tabCount = _tabs.Size();
|
||||
const bool usedManualProfile = (newTerminalArgs != nullptr) &&
|
||||
|
@ -70,7 +70,7 @@ namespace winrt::TerminalApp::implementation
|
|||
newTerminalArgs.Profile().empty());
|
||||
|
||||
// Lookup the name of the color scheme used by this profile.
|
||||
const auto scheme = _settings.GetColorSchemeForProfile(profileGuid);
|
||||
const auto scheme = _settings.GetColorSchemeForProfile(profile);
|
||||
// If they explicitly specified `null` as the scheme (indicating _no_ scheme), log
|
||||
// that as the empty string.
|
||||
const auto schemeName = scheme ? scheme.Name() : L"\0";
|
||||
|
@ -82,48 +82,25 @@ namespace winrt::TerminalApp::implementation
|
|||
TraceLoggingUInt32(1u, "EventVer", "Version of this event"),
|
||||
TraceLoggingUInt32(tabCount, "TabCount", "Count of tabs currently opened in TerminalApp"),
|
||||
TraceLoggingBool(usedManualProfile, "ProfileSpecified", "Whether the new tab specified a profile explicitly"),
|
||||
TraceLoggingGuid(profileGuid, "ProfileGuid", "The GUID of the profile spawned in the new tab"),
|
||||
TraceLoggingGuid(profile.Guid(), "ProfileGuid", "The GUID of the profile spawned in the new tab"),
|
||||
TraceLoggingBool(settings.DefaultSettings().UseAcrylic(), "UseAcrylic", "The acrylic preference from the settings"),
|
||||
TraceLoggingFloat64(settings.DefaultSettings().TintOpacity(), "TintOpacity", "Opacity preference from the settings"),
|
||||
TraceLoggingWideString(settings.DefaultSettings().FontFace().c_str(), "FontFace", "Font face chosen in the settings"),
|
||||
TraceLoggingWideString(schemeName.data(), "SchemeName", "Color scheme set in the settings"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_LOG();
|
||||
CATCH_RETURN();
|
||||
|
||||
// Method Description:
|
||||
// - Creates a new tab with the given settings. If the tab bar is not being
|
||||
// currently displayed, it will be shown.
|
||||
// - Sets up state, event handlers, etc on a tab object that was just made.
|
||||
// Arguments:
|
||||
// - profileGuid: ID to use to lookup profile settings for this connection
|
||||
// - settings: the TerminalSettings object to use to create the TerminalControl with.
|
||||
// - existingConnection: optionally receives a connection from the outside world instead of attempting to create one
|
||||
void TerminalPage::_CreateNewTabFromSettings(GUID profileGuid, const TerminalSettingsCreateResult& settings, TerminalConnection::ITerminalConnection existingConnection)
|
||||
// - newTabImpl: the uninitialized tab.
|
||||
void TerminalPage::_InitializeTab(winrt::com_ptr<TerminalTab> newTabImpl)
|
||||
{
|
||||
// Initialize the new tab
|
||||
// Create a connection based on the values in our settings object if we weren't given one.
|
||||
auto connection = existingConnection ? existingConnection : _CreateConnectionFromSettings(profileGuid, settings.DefaultSettings());
|
||||
|
||||
TerminalConnection::ITerminalConnection debugConnection{ nullptr };
|
||||
if (_settings.GlobalSettings().DebugFeaturesEnabled())
|
||||
{
|
||||
const CoreWindow window = CoreWindow::GetForCurrentThread();
|
||||
const auto rAltState = window.GetKeyState(VirtualKey::RightMenu);
|
||||
const auto lAltState = window.GetKeyState(VirtualKey::LeftMenu);
|
||||
const bool bothAltsPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) &&
|
||||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
|
||||
if (bothAltsPressed)
|
||||
{
|
||||
std::tie(connection, debugConnection) = OpenDebugTapConnection(connection);
|
||||
}
|
||||
}
|
||||
|
||||
// Give term control a child of the settings so that any overrides go in the child
|
||||
// This way, when we do a settings reload we just update the parent and the overrides remain
|
||||
auto term = _InitControl(settings, connection);
|
||||
|
||||
auto newTabImpl = winrt::make_self<TerminalTab>(profileGuid, term);
|
||||
newTabImpl->Initialize();
|
||||
|
||||
// Add the new tab to the list of our tabs.
|
||||
_tabs.Append(*newTabImpl);
|
||||
|
@ -136,7 +113,7 @@ namespace winrt::TerminalApp::implementation
|
|||
_UpdateTabIndices();
|
||||
|
||||
// Hookup our event handlers to the new terminal
|
||||
_RegisterTerminalEvents(term, *newTabImpl);
|
||||
_RegisterTabEvents(*newTabImpl);
|
||||
|
||||
// Don't capture a strong ref to the tab. If the tab is removed as this
|
||||
// is called, we don't really care anymore about handling the event.
|
||||
|
@ -184,14 +161,26 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
});
|
||||
|
||||
newTabImpl->SplitTabRequested([weakTab, weakThis{ get_weak() }]() {
|
||||
auto page{ weakThis.get() };
|
||||
auto tab{ weakTab.get() };
|
||||
|
||||
if (page && tab)
|
||||
{
|
||||
page->_SplitTab(*tab);
|
||||
}
|
||||
});
|
||||
|
||||
auto tabViewItem = newTabImpl->TabViewItem();
|
||||
_tabView.TabItems().Append(tabViewItem);
|
||||
|
||||
// Set this tab's icon to the icon from the user's profile
|
||||
const auto profile = _settings.FindProfile(profileGuid);
|
||||
if (profile != nullptr && !profile.Icon().empty())
|
||||
if (const auto profile{ newTabImpl->GetFocusedProfile() })
|
||||
{
|
||||
newTabImpl->UpdateIcon(profile.Icon());
|
||||
if (!profile.Icon().empty())
|
||||
{
|
||||
newTabImpl->UpdateIcon(profile.Icon());
|
||||
}
|
||||
}
|
||||
|
||||
tabViewItem.PointerReleased({ this, &TerminalPage::_OnTabClick });
|
||||
|
@ -224,19 +213,73 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
});
|
||||
|
||||
if (debugConnection) // this will only be set if global debugging is on and tap is active
|
||||
{
|
||||
auto newControl = _InitControl(settings, debugConnection);
|
||||
_RegisterTerminalEvents(newControl, *newTabImpl);
|
||||
// Split (auto) with the debug tap.
|
||||
newTabImpl->SplitPane(SplitState::Automatic, 0.5f, profileGuid, newControl);
|
||||
}
|
||||
|
||||
// This kicks off TabView::SelectionChanged, in response to which
|
||||
// we'll attach the terminal's Xaml control to the Xaml root.
|
||||
_tabView.SelectedItem(tabViewItem);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Create a new tab using a specified pane as the root.
|
||||
// Arguments:
|
||||
// - pane: The pane to use as the root.
|
||||
void TerminalPage::_CreateNewTabFromPane(std::shared_ptr<Pane> pane)
|
||||
{
|
||||
auto newTabImpl = winrt::make_self<TerminalTab>(pane);
|
||||
_InitializeTab(newTabImpl);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Creates a new tab with the given settings. If the tab bar is not being
|
||||
// currently displayed, it will be shown.
|
||||
// Arguments:
|
||||
// - profile: profile settings for this connection
|
||||
// - settings: the TerminalSettings object to use to create the TerminalControl with.
|
||||
// - existingConnection: optionally receives a connection from the outside world instead of attempting to create one
|
||||
void TerminalPage::_CreateNewTabWithProfileAndSettings(const Profile& profile, const TerminalSettingsCreateResult& settings, TerminalConnection::ITerminalConnection existingConnection)
|
||||
{
|
||||
// Initialize the new tab
|
||||
// Create a connection based on the values in our settings object if we weren't given one.
|
||||
auto connection = existingConnection ? existingConnection : _CreateConnectionFromSettings(profile, settings.DefaultSettings());
|
||||
|
||||
// If we had an `existingConnection`, then this is an inbound handoff from somewhere else.
|
||||
// We need to tell it about our size information so it can match the dimensions of what
|
||||
// we are about to present.
|
||||
if (existingConnection)
|
||||
{
|
||||
connection.Resize(settings.DefaultSettings().InitialRows(), settings.DefaultSettings().InitialCols());
|
||||
}
|
||||
|
||||
TerminalConnection::ITerminalConnection debugConnection{ nullptr };
|
||||
if (_settings.GlobalSettings().DebugFeaturesEnabled())
|
||||
{
|
||||
const CoreWindow window = CoreWindow::GetForCurrentThread();
|
||||
const auto rAltState = window.GetKeyState(VirtualKey::RightMenu);
|
||||
const auto lAltState = window.GetKeyState(VirtualKey::LeftMenu);
|
||||
const bool bothAltsPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) &&
|
||||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
|
||||
if (bothAltsPressed)
|
||||
{
|
||||
std::tie(connection, debugConnection) = OpenDebugTapConnection(connection);
|
||||
}
|
||||
}
|
||||
|
||||
// Give term control a child of the settings so that any overrides go in the child
|
||||
// This way, when we do a settings reload we just update the parent and the overrides remain
|
||||
auto term = _InitControl(settings, connection);
|
||||
|
||||
auto newTabImpl = winrt::make_self<TerminalTab>(profile, term);
|
||||
_RegisterTerminalEvents(term);
|
||||
_InitializeTab(newTabImpl);
|
||||
|
||||
if (debugConnection) // this will only be set if global debugging is on and tap is active
|
||||
{
|
||||
auto newControl = _InitControl(settings, debugConnection);
|
||||
_RegisterTerminalEvents(newControl);
|
||||
// Split (auto) with the debug tap.
|
||||
newTabImpl->SplitPane(SplitState::Automatic, 0.5f, profile, newControl);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Get the icon of the currently focused terminal control, and set its
|
||||
// tab's icon to that icon.
|
||||
|
@ -244,19 +287,9 @@ namespace winrt::TerminalApp::implementation
|
|||
// - tab: the Tab to update the title for.
|
||||
void TerminalPage::_UpdateTabIcon(TerminalTab& tab)
|
||||
{
|
||||
const auto lastFocusedProfileOpt = tab.GetFocusedProfile();
|
||||
if (lastFocusedProfileOpt.has_value())
|
||||
if (const auto profile = tab.GetFocusedProfile())
|
||||
{
|
||||
const auto lastFocusedProfile = lastFocusedProfileOpt.value();
|
||||
const auto matchingProfile = _settings.FindProfile(lastFocusedProfile);
|
||||
if (matchingProfile)
|
||||
{
|
||||
tab.UpdateIcon(matchingProfile.Icon());
|
||||
}
|
||||
else
|
||||
{
|
||||
tab.UpdateIcon({});
|
||||
}
|
||||
tab.UpdateIcon(profile.Icon());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -310,23 +343,16 @@ namespace winrt::TerminalApp::implementation
|
|||
{
|
||||
try
|
||||
{
|
||||
// TODO: GH#5047 - In the future, we should get the Profile of
|
||||
// the focused pane, and use that to build a new instance of the
|
||||
// settings so we can duplicate this tab/pane.
|
||||
// TODO: GH#5047 - We're duplicating the whole profile, which might
|
||||
// be a dangling reference to old settings.
|
||||
//
|
||||
// Currently, if the profile doesn't exist anymore in our
|
||||
// settings, we'll silently do nothing.
|
||||
//
|
||||
// In the future, it will be preferable to just duplicate the
|
||||
// current control's settings, but we can't do that currently,
|
||||
// because we won't be able to create a new instance of the
|
||||
// connection without keeping an instance of the original Profile
|
||||
// object around.
|
||||
// In the future, it may be preferable to just duplicate the
|
||||
// current control's live settings (which will include changes
|
||||
// made through VT).
|
||||
|
||||
const auto& profileGuid = tab.GetFocusedProfile();
|
||||
if (profileGuid.has_value())
|
||||
if (const auto profile = tab.GetFocusedProfile())
|
||||
{
|
||||
const auto settingsCreateResult{ TerminalSettings::CreateWithProfileByID(_settings, profileGuid.value(), *_bindings) };
|
||||
const auto settingsCreateResult{ TerminalSettings::CreateWithProfile(_settings, profile, *_bindings) };
|
||||
const auto workingDirectory = tab.GetActiveTerminalControl().WorkingDirectory();
|
||||
const auto validWorkingDirectory = !workingDirectory.empty();
|
||||
if (validWorkingDirectory)
|
||||
|
@ -334,7 +360,7 @@ namespace winrt::TerminalApp::implementation
|
|||
settingsCreateResult.DefaultSettings().StartingDirectory(workingDirectory);
|
||||
}
|
||||
|
||||
_CreateNewTabFromSettings(profileGuid.value(), settingsCreateResult);
|
||||
_CreateNewTabWithProfileAndSettings(profile, settingsCreateResult);
|
||||
|
||||
const auto runtimeTabText{ tab.GetTabText() };
|
||||
if (!runtimeTabText.empty())
|
||||
|
@ -349,6 +375,20 @@ namespace winrt::TerminalApp::implementation
|
|||
CATCH_LOG();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Sets the specified tab as the focused tab and splits its active pane
|
||||
// Arguments:
|
||||
// - tab: tab to split
|
||||
void TerminalPage::_SplitTab(TerminalTab& tab)
|
||||
{
|
||||
try
|
||||
{
|
||||
_SetFocusedTab(tab);
|
||||
_SplitPane(tab, SplitState::Automatic, SplitType::Duplicate);
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Removes the tab (both TerminalControl and XAML) after prompting for approval
|
||||
// Arguments:
|
||||
|
@ -497,24 +537,25 @@ namespace winrt::TerminalApp::implementation
|
|||
// TerminalPage::_OnTabSelectionChanged
|
||||
// Return Value:
|
||||
// true iff we were able to select that tab index, false otherwise
|
||||
bool TerminalPage::_SelectTab(const uint32_t tabIndex)
|
||||
bool TerminalPage::_SelectTab(uint32_t tabIndex)
|
||||
{
|
||||
if (tabIndex >= 0 && tabIndex < _tabs.Size())
|
||||
{
|
||||
auto tab{ _tabs.GetAt(tabIndex) };
|
||||
if (_startupState == StartupState::InStartup)
|
||||
{
|
||||
_tabView.SelectedItem(tab.TabViewItem());
|
||||
_UpdatedSelectedTab(tab);
|
||||
}
|
||||
else
|
||||
{
|
||||
_SetFocusedTab(tab);
|
||||
}
|
||||
// GH#9369 - if the argument is out of range, then clamp to the number
|
||||
// of available tabs. Previously, we'd just silently do nothing if the
|
||||
// value was greater than the number of tabs.
|
||||
tabIndex = std::clamp(tabIndex, 0u, _tabs.Size() - 1);
|
||||
|
||||
return true;
|
||||
auto tab{ _tabs.GetAt(tabIndex) };
|
||||
if (_startupState == StartupState::InStartup)
|
||||
{
|
||||
_tabView.SelectedItem(tab.TabViewItem());
|
||||
_UpdatedSelectedTab(tab);
|
||||
}
|
||||
return false;
|
||||
else
|
||||
{
|
||||
_SetFocusedTab(tab);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
|
|
@ -5,9 +5,19 @@
|
|||
#include "TabRowControl.h"
|
||||
|
||||
#include "TabRowControl.g.cpp"
|
||||
#include <LibraryResources.h>
|
||||
|
||||
using namespace winrt::Windows::ApplicationModel::DataTransfer;
|
||||
|
||||
using namespace winrt;
|
||||
using namespace winrt::Microsoft::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Text;
|
||||
|
||||
namespace winrt
|
||||
{
|
||||
namespace MUX = Microsoft::UI::Xaml;
|
||||
namespace WUX = Windows::UI::Xaml;
|
||||
}
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
|
@ -23,4 +33,53 @@ namespace winrt::TerminalApp::implementation
|
|||
void TabRowControl::OnNewTabButtonClick(IInspectable const&, Controls::SplitButtonClickEventArgs const&)
|
||||
{
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Bound in Drag&Drop of the Xaml editor to the [+] button.
|
||||
// Arguments:
|
||||
// <unused>
|
||||
void TabRowControl::OnNewTabButtonDrop(IInspectable const&, winrt::Windows::UI::Xaml::DragEventArgs const&)
|
||||
{
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Bound in Drag-over of the Xaml editor to the [+] button.
|
||||
// Allows drop of 'StorageItems' which will be used as StartingDirectory
|
||||
// Arguments:
|
||||
// - <unused>
|
||||
// - e: DragEventArgs which hold the items
|
||||
void TabRowControl::OnNewTabButtonDragOver(IInspectable const&, winrt::Windows::UI::Xaml::DragEventArgs const& e)
|
||||
{
|
||||
// We can only handle drag/dropping StorageItems (files).
|
||||
// If the format on the clipboard is anything else, returning
|
||||
// early here will prevent the drag/drop from doing anything.
|
||||
if (!e.DataView().Contains(StandardDataFormats::StorageItems()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure to set the AcceptedOperation, so that we can later receive the path in the Drop event
|
||||
e.AcceptedOperation(DataPackageOperation::Copy);
|
||||
|
||||
const auto modifiers = static_cast<uint32_t>(e.Modifiers());
|
||||
if (WI_IsFlagSet(modifiers, static_cast<uint32_t>(DragDrop::DragDropModifiers::Alt)))
|
||||
{
|
||||
e.DragUIOverride().Caption(RS_(L"DropPathTabSplit/Text"));
|
||||
}
|
||||
else if (WI_IsFlagSet(modifiers, static_cast<uint32_t>(DragDrop::DragDropModifiers::Shift)))
|
||||
{
|
||||
e.DragUIOverride().Caption(RS_(L"DropPathTabNewWindow/Text"));
|
||||
}
|
||||
else
|
||||
{
|
||||
e.DragUIOverride().Caption(RS_(L"DropPathTabRun/Text"));
|
||||
}
|
||||
|
||||
// Sets if the caption is visible
|
||||
e.DragUIOverride().IsCaptionVisible(true);
|
||||
// Sets if the dragged content is visible
|
||||
e.DragUIOverride().IsContentVisible(false);
|
||||
// Sets if the glyph is visible
|
||||
e.DragUIOverride().IsGlyphVisible(false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@ namespace winrt::TerminalApp::implementation
|
|||
TabRowControl();
|
||||
|
||||
void OnNewTabButtonClick(Windows::Foundation::IInspectable const& sender, Microsoft::UI::Xaml::Controls::SplitButtonClickEventArgs const& args);
|
||||
void OnNewTabButtonDrop(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::DragEventArgs const& e);
|
||||
void OnNewTabButtonDragOver(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::DragEventArgs const& e);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -25,11 +25,14 @@
|
|||
x:Uid="NewTabSplitButton"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Stretch"
|
||||
AllowDrop="True"
|
||||
AutomationProperties.AccessibilityView="Control"
|
||||
BorderThickness="0"
|
||||
Click="OnNewTabButtonClick"
|
||||
Content=""
|
||||
CornerRadius="{Binding Source={ThemeResource OverlayCornerRadius}, Converter={StaticResource TopCornerRadiusFilterConverter}}"
|
||||
DragOver="OnNewTabButtonDragOver"
|
||||
Drop="OnNewTabButtonDrop"
|
||||
FontFamily="Segoe MDL2 Assets"
|
||||
FontSize="12"
|
||||
FontWeight="SemiLight"
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "TaskbarState.h"
|
||||
#include "TaskbarState.g.cpp"
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// Default to unset, 0%.
|
||||
TaskbarState::TaskbarState() :
|
||||
TaskbarState(0, 0){};
|
||||
|
||||
TaskbarState::TaskbarState(const uint64_t dispatchTypesState, const uint64_t progressParam) :
|
||||
_State{ dispatchTypesState },
|
||||
_Progress{ progressParam } {}
|
||||
|
||||
uint64_t TaskbarState::Priority() const
|
||||
{
|
||||
// This seemingly nonsensical ordering is from
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-itaskbarlist3-setprogressstate#how-the-taskbar-button-chooses-the-progress-indicator-for-a-group
|
||||
switch (_State)
|
||||
{
|
||||
case 0: // Clear = 0,
|
||||
return 5;
|
||||
case 1: // Set = 1,
|
||||
return 3;
|
||||
case 2: // Error = 2,
|
||||
return 1;
|
||||
case 3: // Indeterminate = 3,
|
||||
return 4;
|
||||
case 4: // Paused = 4
|
||||
return 2;
|
||||
}
|
||||
// Here, return 6, to definitely be greater than all the other valid values.
|
||||
// This should never really happen.
|
||||
return 6;
|
||||
}
|
||||
|
||||
int TaskbarState::ComparePriority(const winrt::TerminalApp::TaskbarState& lhs, const winrt::TerminalApp::TaskbarState& rhs)
|
||||
{
|
||||
return lhs.Priority() < rhs.Priority();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
#include "inc/cppwinrt_utils.h"
|
||||
#include "TaskbarState.g.h"
|
||||
|
||||
// fwdecl unittest classes
|
||||
namespace TerminalAppLocalTests
|
||||
{
|
||||
class TabTests;
|
||||
};
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
struct TaskbarState : TaskbarStateT<TaskbarState>
|
||||
{
|
||||
public:
|
||||
TaskbarState();
|
||||
TaskbarState(const uint64_t dispatchTypesState, const uint64_t progress);
|
||||
|
||||
static int ComparePriority(const winrt::TerminalApp::TaskbarState& lhs, const winrt::TerminalApp::TaskbarState& rhs);
|
||||
|
||||
uint64_t Priority() const;
|
||||
|
||||
WINRT_PROPERTY(uint64_t, State, 0);
|
||||
WINRT_PROPERTY(uint64_t, Progress, 0);
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::TerminalApp::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(TaskbarState);
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
[default_interface] runtimeclass TaskbarState
|
||||
{
|
||||
TaskbarState();
|
||||
TaskbarState(UInt64 dispatchTypesState, UInt64 progress);
|
||||
|
||||
UInt64 State{ get; };
|
||||
UInt64 Progress{ get; };
|
||||
UInt64 Priority { get; };
|
||||
}
|
||||
}
|
|
@ -53,7 +53,7 @@
|
|||
<Page Include="TabRowControl.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="TabHeaderControl.xaml">
|
||||
<Page Include="TabHeaderControl.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="HighlightedTextControl.xaml">
|
||||
|
@ -74,6 +74,7 @@
|
|||
<ClInclude Include="Commandline.h" />
|
||||
<ClInclude Include="CommandLinePaletteItem.h" />
|
||||
<ClInclude Include="Jumplist.h" />
|
||||
<ClInclude Include="LanguageProfileNotifier.h" />
|
||||
<ClInclude Include="MinMaxCloseControl.h">
|
||||
<DependentUpon>MinMaxCloseControl.xaml</DependentUpon>
|
||||
</ClInclude>
|
||||
|
@ -89,6 +90,9 @@
|
|||
<DependentUpon>TabBase.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TabPaletteItem.h" />
|
||||
<ClInclude Include="TaskbarState.h">
|
||||
<DependentUpon>TaskbarState.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TerminalTab.h">
|
||||
<DependentUpon>TerminalTab.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
|
@ -112,7 +116,7 @@
|
|||
<ClInclude Include="HighlightedTextControl.h">
|
||||
<DependentUpon>HighlightedTextControl.xaml</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HighlightedText.h" />
|
||||
<ClInclude Include="HighlightedText.h" />
|
||||
<ClInclude Include="ColorPickupFlyout.h">
|
||||
<DependentUpon>ColorPickupFlyout.xaml</DependentUpon>
|
||||
</ClInclude>
|
||||
|
@ -139,7 +143,7 @@
|
|||
<ClInclude Include="AppLogic.h">
|
||||
<DependentUpon>AppLogic.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Toast.h"/>
|
||||
<ClInclude Include="Toast.h" />
|
||||
</ItemGroup>
|
||||
<!-- ========================= Cpp Files ======================== -->
|
||||
<ItemGroup>
|
||||
|
@ -149,6 +153,7 @@
|
|||
<ClCompile Include="AppCommandlineArgs.cpp" />
|
||||
<ClCompile Include="Commandline.cpp" />
|
||||
<ClCompile Include="Jumplist.cpp" />
|
||||
<ClCompile Include="LanguageProfileNotifier.cpp" />
|
||||
<ClCompile Include="MinMaxCloseControl.cpp">
|
||||
<DependentUpon>MinMaxCloseControl.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
|
@ -164,6 +169,9 @@
|
|||
<DependentUpon>TabBase.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TabPaletteItem.cpp" />
|
||||
<ClCompile Include="TaskbarState.cpp">
|
||||
<DependentUpon>TaskbarState.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TerminalTab.cpp">
|
||||
<DependentUpon>TerminalTab.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
|
@ -195,7 +203,7 @@
|
|||
<ClCompile Include="HighlightedTextControl.cpp">
|
||||
<DependentUpon>HighlightedTextControl.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HighlightedText.cpp" />
|
||||
<ClCompile Include="HighlightedText.cpp" />
|
||||
<ClCompile Include="ColorPickupFlyout.cpp">
|
||||
<DependentUpon>ColorPickupFlyout.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
|
@ -256,6 +264,7 @@
|
|||
</Midl>
|
||||
<Midl Include="TabBase.idl" />
|
||||
<Midl Include="TabPaletteItem.idl" />
|
||||
<Midl Include="TaskbarState.idl" />
|
||||
<Midl Include="TerminalTab.idl" />
|
||||
<Midl Include="TerminalPage.idl">
|
||||
<DependentUpon>TerminalPage.xaml</DependentUpon>
|
||||
|
@ -280,7 +289,7 @@
|
|||
<DependentUpon>HighlightedTextControl.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Midl>
|
||||
<Midl Include="HighlightedText.idl" />
|
||||
<Midl Include="HighlightedText.idl" />
|
||||
<Midl Include="ColorPickupFlyout.idl">
|
||||
<DependentUpon>ColorPickupFlyout.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
|
@ -371,13 +380,13 @@
|
|||
<!-- ========================= Globals ======================== -->
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
|
||||
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" />
|
||||
</Target>
|
||||
|
||||
|
|
|
@ -13,9 +13,6 @@
|
|||
<ClCompile Include="Pane.cpp">
|
||||
<Filter>pane</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Tab.cpp">
|
||||
<Filter>tab</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Pane.LayoutSizeNode.cpp">
|
||||
<Filter>pane</Filter>
|
||||
</ClCompile>
|
||||
|
@ -23,7 +20,6 @@
|
|||
<ClCompile Include="Commandline.cpp" />
|
||||
<ClCompile Include="ColorHelper.cpp" />
|
||||
<ClCompile Include="DebugTapConnection.cpp" />
|
||||
<ClCompile Include="Utils.cpp" />
|
||||
<ClCompile Include="Jumplist.cpp" />
|
||||
<ClCompile Include="Tab.cpp">
|
||||
<Filter>tab</Filter>
|
||||
|
@ -49,10 +45,10 @@
|
|||
<ClCompile Include="HighlightedText.cpp">
|
||||
<Filter>highlightedText</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Toast.cpp" />
|
||||
<ClCompile Include="LanguageProfileNotifier.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Utils.h" />
|
||||
<ClInclude Include="TerminalWarnings.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="App.base.h">
|
||||
<Filter>app</Filter>
|
||||
|
@ -60,9 +56,6 @@
|
|||
<ClInclude Include="Pane.h">
|
||||
<Filter>pane</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Tab.h">
|
||||
<Filter>tab</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AppCommandlineArgs.h" />
|
||||
<ClInclude Include="Commandline.h" />
|
||||
<ClInclude Include="DebugTapConnection.h" />
|
||||
|
@ -92,14 +85,13 @@
|
|||
<ClInclude Include="HighlightedText.h">
|
||||
<Filter>highlightedText</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Toast.h" />
|
||||
<ClInclude Include="LanguageProfileNotifier.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Midl Include="AppLogic.idl">
|
||||
<Filter>app</Filter>
|
||||
</Midl>
|
||||
<Midl Include="ActionArgs.idl">
|
||||
<Filter>settings</Filter>
|
||||
</Midl>
|
||||
<Midl Include="AppKeyBindings.idl">
|
||||
<Filter>settings</Filter>
|
||||
</Midl>
|
||||
|
@ -107,9 +99,6 @@
|
|||
<Filter>settings</Filter>
|
||||
</Midl>
|
||||
<Midl Include="IDirectKeyListener.idl" />
|
||||
<Midl Include="ITab.idl">
|
||||
<Filter>tab</Filter>
|
||||
</Midl>
|
||||
<Midl Include="SettingsTab.idl">
|
||||
<Filter>tab</Filter>
|
||||
</Midl>
|
||||
|
@ -125,6 +114,9 @@
|
|||
<Midl Include="TerminalTabStatus.idl">
|
||||
<Filter>tab</Filter>
|
||||
</Midl>
|
||||
<Midl Include="PaletteItemTemplateSelector.idl" />
|
||||
<Midl Include="TabBase.idl" />
|
||||
<Midl Include="EmptyStringVisibilityConverter.idl" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
@ -160,9 +152,6 @@
|
|||
<Midl Include="ActionPaletteItem.idl">
|
||||
<Filter>commandPalette</Filter>
|
||||
</Midl>
|
||||
<Midl Include="FilterableListItem.idl">
|
||||
<Filter>commandPalette</Filter>
|
||||
</Midl>
|
||||
<Midl Include="CommandLinePaletteItem.idl">
|
||||
<Filter>commandPalette</Filter>
|
||||
</Midl>
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "Utils.h"
|
||||
#include "../../types/inc/utils.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <LibraryResources.h>
|
||||
|
||||
#include "TerminalPage.g.cpp"
|
||||
|
@ -18,6 +19,8 @@
|
|||
#include "RenameWindowRequestedArgs.g.cpp"
|
||||
#include "../inc/WindowingBehavior.h"
|
||||
|
||||
#include <til/latch.h>
|
||||
|
||||
using namespace winrt;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
|
@ -139,6 +142,44 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
CATCH_LOG();
|
||||
|
||||
if (_settings.GlobalSettings().UseAcrylicInTabRow())
|
||||
{
|
||||
const auto res = Application::Current().Resources();
|
||||
|
||||
const auto lightKey = winrt::box_value(L"Light");
|
||||
const auto darkKey = winrt::box_value(L"Dark");
|
||||
const auto tabViewBackgroundKey = winrt::box_value(L"TabViewBackground");
|
||||
|
||||
for (auto const& dictionary : res.MergedDictionaries())
|
||||
{
|
||||
// Don't change MUX resources
|
||||
if (dictionary.Source())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto const& kvPair : dictionary.ThemeDictionaries())
|
||||
{
|
||||
const auto themeDictionary = kvPair.Value().as<winrt::Windows::UI::Xaml::ResourceDictionary>();
|
||||
|
||||
if (themeDictionary.HasKey(tabViewBackgroundKey))
|
||||
{
|
||||
const auto backgroundSolidBrush = themeDictionary.Lookup(tabViewBackgroundKey).as<Media::SolidColorBrush>();
|
||||
|
||||
const til::color backgroundColor = backgroundSolidBrush.Color();
|
||||
|
||||
const auto acrylicBrush = Media::AcrylicBrush();
|
||||
acrylicBrush.BackgroundSource(Media::AcrylicBackgroundSource::HostBackdrop);
|
||||
acrylicBrush.FallbackColor(backgroundColor);
|
||||
acrylicBrush.TintColor(backgroundColor);
|
||||
acrylicBrush.TintOpacity(0.5);
|
||||
|
||||
themeDictionary.Insert(tabViewBackgroundKey, acrylicBrush);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_tabRow.PointerMoved({ get_weak(), &TerminalPage::_RestorePointerCursorHandler });
|
||||
_tabView.CanReorderTabs(!isElevated);
|
||||
_tabView.CanDragTabs(!isElevated);
|
||||
|
@ -172,40 +213,13 @@ namespace winrt::TerminalApp::implementation
|
|||
_newTabButton.Click([weakThis{ get_weak() }](auto&&, auto&&) {
|
||||
if (auto page{ weakThis.get() })
|
||||
{
|
||||
// if alt is pressed, open a pane
|
||||
const CoreWindow window = CoreWindow::GetForCurrentThread();
|
||||
const auto rAltState = window.GetKeyState(VirtualKey::RightMenu);
|
||||
const auto lAltState = window.GetKeyState(VirtualKey::LeftMenu);
|
||||
const bool altPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) ||
|
||||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
|
||||
|
||||
const auto shiftState{ window.GetKeyState(VirtualKey::Shift) };
|
||||
const auto rShiftState = window.GetKeyState(VirtualKey::RightShift);
|
||||
const auto lShiftState = window.GetKeyState(VirtualKey::LeftShift);
|
||||
const auto shiftPressed{ WI_IsFlagSet(shiftState, CoreVirtualKeyStates::Down) ||
|
||||
WI_IsFlagSet(lShiftState, CoreVirtualKeyStates::Down) ||
|
||||
WI_IsFlagSet(rShiftState, CoreVirtualKeyStates::Down) };
|
||||
|
||||
// Check for DebugTap
|
||||
bool debugTap = page->_settings.GlobalSettings().DebugFeaturesEnabled() &&
|
||||
WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) &&
|
||||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
|
||||
|
||||
if (altPressed && !debugTap)
|
||||
{
|
||||
page->_SplitPane(SplitState::Automatic,
|
||||
SplitType::Manual,
|
||||
0.5f,
|
||||
nullptr);
|
||||
}
|
||||
else if (shiftPressed && !debugTap)
|
||||
{
|
||||
page->_OpenNewWindow(false, NewTerminalArgs());
|
||||
}
|
||||
else
|
||||
{
|
||||
page->_OpenNewTab(nullptr);
|
||||
}
|
||||
page->_OpenNewTerminal(NewTerminalArgs());
|
||||
}
|
||||
});
|
||||
_newTabButton.Drop([weakThis{ get_weak() }](Windows::Foundation::IInspectable const&, winrt::Windows::UI::Xaml::DragEventArgs e) {
|
||||
if (auto page{ weakThis.get() })
|
||||
{
|
||||
page->NewTerminalByDrop(e);
|
||||
}
|
||||
});
|
||||
_tabView.SelectionChanged({ this, &TerminalPage::_OnTabSelectionChanged });
|
||||
|
@ -262,6 +276,36 @@ namespace winrt::TerminalApp::implementation
|
|||
CATCH_LOG();
|
||||
}
|
||||
|
||||
winrt::fire_and_forget TerminalPage::NewTerminalByDrop(winrt::Windows::UI::Xaml::DragEventArgs& e)
|
||||
{
|
||||
Windows::Foundation::Collections::IVectorView<Windows::Storage::IStorageItem> items;
|
||||
try
|
||||
{
|
||||
items = co_await e.DataView().GetStorageItemsAsync();
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
if (items.Size() == 1)
|
||||
{
|
||||
std::filesystem::path path(items.GetAt(0).Path().c_str());
|
||||
if (!std::filesystem::is_directory(path))
|
||||
{
|
||||
path = path.parent_path();
|
||||
}
|
||||
|
||||
NewTerminalArgs args;
|
||||
args.StartingDirectory(winrt::hstring{ path.wstring() });
|
||||
this->_OpenNewTerminal(args);
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalAppProvider,
|
||||
"NewTabByDragDrop",
|
||||
TraceLoggingDescription("Event emitted when the user drag&drops onto the new tab button"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - This method is called once command palette action was chosen for dispatching
|
||||
// We'll use this event to dispatch this command.
|
||||
|
@ -344,34 +388,12 @@ namespace winrt::TerminalApp::implementation
|
|||
winrt::Microsoft::Terminal::TerminalConnection::ConptyConnection::StartInboundListener();
|
||||
}
|
||||
// If we failed to start the listener, it will throw.
|
||||
// We should fail fast here or the Terminal will be in a very strange state.
|
||||
// We only start the listener if the Terminal was started with the COM server
|
||||
// `-Embedding` flag and we make no tabs as a result.
|
||||
// Therefore, if the listener cannot start itself up to make that tab with
|
||||
// the inbound connection that caused the COM activation in the first place...
|
||||
// we would be left with an empty terminal frame with no tabs.
|
||||
// Instead, crash out so COM sees the server die and things unwind
|
||||
// without a weird empty frame window.
|
||||
// We don't want to fail fast here because if a peasant has some trouble with
|
||||
// starting the listener, we don't want it to crash and take all its tabs down
|
||||
// with it.
|
||||
catch (...)
|
||||
{
|
||||
// However, we cannot always fail fast because of MSFT:33501832. Sometimes the COM catalog
|
||||
// tears the state between old and new versions and fails here for that reason.
|
||||
// As we're always becoming an inbound server in the monarch, even when COM didn't strictly
|
||||
// ask us yet...we might just crash always.
|
||||
// Instead... we're going to differentiate. If COM started us... we will fail fast
|
||||
// so it sees the process die and falls back.
|
||||
// If we were just starting normally as a Monarch and opportunistically listening for
|
||||
// inbound connections... then we'll just log the failure and move on assuming
|
||||
// the version state is torn and will fix itself whenever the packaging upgrade
|
||||
// tasks decide to clean up.
|
||||
if (_isEmbeddingInboundListener)
|
||||
{
|
||||
FAIL_FAST_CAUGHT_EXCEPTION();
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
}
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -435,6 +457,11 @@ namespace winrt::TerminalApp::implementation
|
|||
co_return;
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
_GetActiveControl().Focus(FocusState::Programmatic);
|
||||
}
|
||||
if (initial)
|
||||
{
|
||||
|
@ -622,43 +649,7 @@ namespace winrt::TerminalApp::implementation
|
|||
if (auto page{ weakThis.get() })
|
||||
{
|
||||
NewTerminalArgs newTerminalArgs{ profileIndex };
|
||||
|
||||
// if alt is pressed, open a pane
|
||||
const CoreWindow window = CoreWindow::GetForCurrentThread();
|
||||
const auto rAltState = window.GetKeyState(VirtualKey::RightMenu);
|
||||
const auto lAltState = window.GetKeyState(VirtualKey::LeftMenu);
|
||||
const bool altPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) ||
|
||||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
|
||||
|
||||
const auto shiftState{ window.GetKeyState(VirtualKey::Shift) };
|
||||
const auto rShiftState = window.GetKeyState(VirtualKey::RightShift);
|
||||
const auto lShiftState = window.GetKeyState(VirtualKey::LeftShift);
|
||||
const auto shiftPressed{ WI_IsFlagSet(shiftState, CoreVirtualKeyStates::Down) ||
|
||||
WI_IsFlagSet(lShiftState, CoreVirtualKeyStates::Down) ||
|
||||
WI_IsFlagSet(rShiftState, CoreVirtualKeyStates::Down) };
|
||||
|
||||
// Check for DebugTap
|
||||
bool debugTap = page->_settings.GlobalSettings().DebugFeaturesEnabled() &&
|
||||
WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) &&
|
||||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
|
||||
|
||||
if (altPressed && !debugTap)
|
||||
{
|
||||
page->_SplitPane(SplitState::Automatic,
|
||||
SplitType::Manual,
|
||||
0.5f,
|
||||
newTerminalArgs);
|
||||
}
|
||||
else if (shiftPressed && !debugTap)
|
||||
{
|
||||
// Manually fill in the evaluated profile.
|
||||
newTerminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(page->_settings.GetProfileForArgs(newTerminalArgs)));
|
||||
page->_OpenNewWindow(false, newTerminalArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
page->_OpenNewTab(newTerminalArgs);
|
||||
}
|
||||
page->_OpenNewTerminal(newTerminalArgs);
|
||||
}
|
||||
});
|
||||
newTabFlyout.Items().Append(profileMenuItem);
|
||||
|
@ -752,6 +743,54 @@ namespace winrt::TerminalApp::implementation
|
|||
_newTabButton.Flyout().ShowAt(_newTabButton);
|
||||
}
|
||||
|
||||
void TerminalPage::_OpenNewTerminal(const NewTerminalArgs newTerminalArgs)
|
||||
{
|
||||
// if alt is pressed, open a pane
|
||||
const CoreWindow window = CoreWindow::GetForCurrentThread();
|
||||
const auto rAltState = window.GetKeyState(VirtualKey::RightMenu);
|
||||
const auto lAltState = window.GetKeyState(VirtualKey::LeftMenu);
|
||||
const bool altPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) ||
|
||||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
|
||||
|
||||
const auto shiftState{ window.GetKeyState(VirtualKey::Shift) };
|
||||
const auto rShiftState = window.GetKeyState(VirtualKey::RightShift);
|
||||
const auto lShiftState = window.GetKeyState(VirtualKey::LeftShift);
|
||||
const auto shiftPressed{ WI_IsFlagSet(shiftState, CoreVirtualKeyStates::Down) ||
|
||||
WI_IsFlagSet(lShiftState, CoreVirtualKeyStates::Down) ||
|
||||
WI_IsFlagSet(rShiftState, CoreVirtualKeyStates::Down) };
|
||||
|
||||
// Check for DebugTap
|
||||
bool debugTap = this->_settings.GlobalSettings().DebugFeaturesEnabled() &&
|
||||
WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) &&
|
||||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
|
||||
|
||||
if (altPressed && !debugTap)
|
||||
{
|
||||
this->_SplitPane(SplitState::Automatic,
|
||||
SplitType::Manual,
|
||||
0.5f,
|
||||
newTerminalArgs);
|
||||
}
|
||||
else if (shiftPressed && !debugTap)
|
||||
{
|
||||
// Manually fill in the evaluated profile.
|
||||
if (newTerminalArgs.ProfileIndex() != nullptr)
|
||||
{
|
||||
// We want to promote the index to a GUID because there is no "launch to profile index" command.
|
||||
const auto profile = _settings.GetProfileForArgs(newTerminalArgs);
|
||||
if (profile)
|
||||
{
|
||||
newTerminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(profile.Guid()));
|
||||
}
|
||||
}
|
||||
this->_OpenNewWindow(false, newTerminalArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_IF_FAILED(this->_OpenNewTab(newTerminalArgs));
|
||||
}
|
||||
}
|
||||
|
||||
winrt::fire_and_forget TerminalPage::_RemoveOnCloseRoutine(Microsoft::UI::Xaml::Controls::TabViewItem tabViewItem, winrt::com_ptr<TerminalPage> page)
|
||||
{
|
||||
co_await winrt::resume_foreground(page->_tabView.Dispatcher());
|
||||
|
@ -765,14 +804,18 @@ namespace winrt::TerminalApp::implementation
|
|||
// Method Description:
|
||||
// - Creates a new connection based on the profile settings
|
||||
// Arguments:
|
||||
// - the profile GUID we want the settings from
|
||||
// - the profile we want the settings from
|
||||
// - the terminal settings
|
||||
// Return value:
|
||||
// - the desired connection
|
||||
TerminalConnection::ITerminalConnection TerminalPage::_CreateConnectionFromSettings(GUID profileGuid,
|
||||
TerminalConnection::ITerminalConnection TerminalPage::_CreateConnectionFromSettings(Profile profile,
|
||||
TerminalSettings settings)
|
||||
{
|
||||
const auto profile = _settings.FindProfile(profileGuid);
|
||||
if (!profile)
|
||||
{
|
||||
// Use the default profile if we didn't get one as an argument.
|
||||
profile = _settings.FindProfile(_settings.GlobalSettings().DefaultProfile());
|
||||
}
|
||||
|
||||
TerminalConnection::ITerminalConnection connection{ nullptr };
|
||||
|
||||
|
@ -785,18 +828,20 @@ namespace winrt::TerminalApp::implementation
|
|||
// TODO GH#4661: Replace this with directly using the AzCon when our VT is better
|
||||
std::filesystem::path azBridgePath{ wil::GetModuleFileNameW<std::wstring>(nullptr) };
|
||||
azBridgePath.replace_filename(L"TerminalAzBridge.exe");
|
||||
connection = TerminalConnection::ConptyConnection(azBridgePath.wstring(),
|
||||
L".",
|
||||
L"Azure",
|
||||
nullptr,
|
||||
settings.InitialRows(),
|
||||
settings.InitialCols(),
|
||||
winrt::guid());
|
||||
connection = TerminalConnection::ConptyConnection();
|
||||
connection.Initialize(TerminalConnection::ConptyConnection::CreateSettings(azBridgePath.wstring(),
|
||||
L".",
|
||||
L"Azure",
|
||||
nullptr,
|
||||
::base::saturated_cast<uint32_t>(settings.InitialRows()),
|
||||
::base::saturated_cast<uint32_t>(settings.InitialCols()),
|
||||
winrt::guid()));
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
std::wstring guidWString = Utils::GuidToString(profileGuid);
|
||||
// profile is guaranteed to exist here
|
||||
std::wstring guidWString = Utils::GuidToString(profile.Guid());
|
||||
|
||||
StringMap envMap{};
|
||||
envMap.Insert(L"WT_PROFILE_ID", guidWString);
|
||||
|
@ -817,18 +862,24 @@ namespace winrt::TerminalApp::implementation
|
|||
// construction, because the connection might not spawn the child
|
||||
// process until later, on another thread, after we've already
|
||||
// restored the CWD to it's original value.
|
||||
std::wstring cwdString{ wil::GetCurrentDirectoryW<std::wstring>() };
|
||||
std::filesystem::path cwd{ cwdString };
|
||||
cwd /= settings.StartingDirectory().c_str();
|
||||
winrt::hstring newWorkingDirectory{ settings.StartingDirectory() };
|
||||
if (newWorkingDirectory.size() <= 1 ||
|
||||
!(newWorkingDirectory[0] == L'~' || newWorkingDirectory[0] == L'/'))
|
||||
{ // We only want to resolve the new WD against the CWD if it doesn't look like a Linux path (see GH#592)
|
||||
std::wstring cwdString{ wil::GetCurrentDirectoryW<std::wstring>() };
|
||||
std::filesystem::path cwd{ cwdString };
|
||||
cwd /= settings.StartingDirectory().c_str();
|
||||
newWorkingDirectory = winrt::hstring{ cwd.wstring() };
|
||||
}
|
||||
|
||||
auto conhostConn = TerminalConnection::ConptyConnection(
|
||||
settings.Commandline(),
|
||||
winrt::hstring{ cwd.c_str() },
|
||||
settings.StartingTitle(),
|
||||
envMap.GetView(),
|
||||
settings.InitialRows(),
|
||||
settings.InitialCols(),
|
||||
winrt::guid());
|
||||
auto conhostConn = TerminalConnection::ConptyConnection();
|
||||
conhostConn.Initialize(TerminalConnection::ConptyConnection::CreateSettings(settings.Commandline(),
|
||||
newWorkingDirectory,
|
||||
settings.StartingTitle(),
|
||||
envMap.GetView(),
|
||||
::base::saturated_cast<uint32_t>(settings.InitialRows()),
|
||||
::base::saturated_cast<uint32_t>(settings.InitialCols()),
|
||||
winrt::guid()));
|
||||
|
||||
sessionGuid = conhostConn.Guid();
|
||||
connection = conhostConn;
|
||||
|
@ -839,7 +890,7 @@ namespace winrt::TerminalApp::implementation
|
|||
"ConnectionCreated",
|
||||
TraceLoggingDescription("Event emitted upon the creation of a connection"),
|
||||
TraceLoggingGuid(connectionType, "ConnectionTypeGuid", "The type of the connection"),
|
||||
TraceLoggingGuid(profileGuid, "ProfileGuid", "The profile's GUID"),
|
||||
TraceLoggingGuid(profile.Guid(), "ProfileGuid", "The profile's GUID"),
|
||||
TraceLoggingGuid(sessionGuid, "SessionGuid", "The WT_SESSION's GUID"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
|
||||
|
@ -914,12 +965,14 @@ namespace winrt::TerminalApp::implementation
|
|||
// - <none>
|
||||
void TerminalPage::_KeyDownHandler(Windows::Foundation::IInspectable const& /*sender*/, Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e)
|
||||
{
|
||||
auto key = e.OriginalKey();
|
||||
auto const ctrlDown = WI_IsFlagSet(CoreWindow::GetForCurrentThread().GetKeyState(winrt::Windows::System::VirtualKey::Control), CoreVirtualKeyStates::Down);
|
||||
auto const altDown = WI_IsFlagSet(CoreWindow::GetForCurrentThread().GetKeyState(winrt::Windows::System::VirtualKey::Menu), CoreVirtualKeyStates::Down);
|
||||
auto const shiftDown = WI_IsFlagSet(CoreWindow::GetForCurrentThread().GetKeyState(winrt::Windows::System::VirtualKey::Shift), CoreVirtualKeyStates::Down);
|
||||
const auto key = e.OriginalKey();
|
||||
const auto scanCode = e.KeyStatus().ScanCode;
|
||||
const auto coreWindow = CoreWindow::GetForCurrentThread();
|
||||
const auto ctrlDown = WI_IsFlagSet(coreWindow.GetKeyState(winrt::Windows::System::VirtualKey::Control), CoreVirtualKeyStates::Down);
|
||||
const auto altDown = WI_IsFlagSet(coreWindow.GetKeyState(winrt::Windows::System::VirtualKey::Menu), CoreVirtualKeyStates::Down);
|
||||
const auto shiftDown = WI_IsFlagSet(coreWindow.GetKeyState(winrt::Windows::System::VirtualKey::Shift), CoreVirtualKeyStates::Down);
|
||||
|
||||
winrt::Microsoft::Terminal::Control::KeyChord kc{ ctrlDown, altDown, shiftDown, static_cast<int32_t>(key) };
|
||||
winrt::Microsoft::Terminal::Control::KeyChord kc{ ctrlDown, altDown, shiftDown, false, static_cast<int32_t>(key), static_cast<int32_t>(scanCode) };
|
||||
if (const auto cmd{ _settings.ActionMap().GetActionByKeyChord(kc) })
|
||||
{
|
||||
if (CommandPalette().Visibility() == Visibility::Visible && cmd.ActionAndArgs().Action() != ShortcutAction::ToggleCommandPalette)
|
||||
|
@ -980,11 +1033,9 @@ namespace winrt::TerminalApp::implementation
|
|||
// handle. This includes:
|
||||
// * the Copy and Paste events, for setting and retrieving clipboard data
|
||||
// on the right thread
|
||||
// * the TitleChanged event, for changing the text of the tab
|
||||
// Arguments:
|
||||
// - term: The newly created TermControl to connect the events for
|
||||
// - hostingTab: The Tab that's hosting this TermControl instance
|
||||
void TerminalPage::_RegisterTerminalEvents(TermControl term, TerminalTab& hostingTab)
|
||||
void TerminalPage::_RegisterTerminalEvents(TermControl term)
|
||||
{
|
||||
term.RaiseNotice({ this, &TerminalPage::_ControlNoticeRaisedHandler });
|
||||
|
||||
|
@ -999,10 +1050,20 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
term.HidePointerCursor({ get_weak(), &TerminalPage::_HidePointerCursorHandler });
|
||||
term.RestorePointerCursor({ get_weak(), &TerminalPage::_RestorePointerCursorHandler });
|
||||
// Add an event handler for when the terminal or tab wants to set a
|
||||
// progress indicator on the taskbar
|
||||
term.SetTaskbarProgress({ get_weak(), &TerminalPage::_SetTaskbarProgressHandler });
|
||||
}
|
||||
|
||||
// Bind Tab events to the TermControl and the Tab's Pane
|
||||
hostingTab.Initialize(term);
|
||||
|
||||
// Method Description:
|
||||
// - Connects event handlers to the TerminalTab for events that we want to
|
||||
// handle. This includes:
|
||||
// * the TitleChanged event, for changing the text of the tab
|
||||
// * the Color{Selected,Cleared} events to change the color of a tab.
|
||||
// Arguments:
|
||||
// - hostingTab: The Tab that's hosting this TermControl instance
|
||||
void TerminalPage::_RegisterTabEvents(TerminalTab& hostingTab)
|
||||
{
|
||||
auto weakTab{ hostingTab.get_weak() };
|
||||
auto weakThis{ get_weak() };
|
||||
// PropertyChanged is the generic mechanism by which the Tab
|
||||
|
@ -1054,7 +1115,6 @@ namespace winrt::TerminalApp::implementation
|
|||
// Add an event handler for when the terminal or tab wants to set a
|
||||
// progress indicator on the taskbar
|
||||
hostingTab.TaskbarProgressChanged({ get_weak(), &TerminalPage::_SetTaskbarProgressHandler });
|
||||
term.SetTaskbarProgress({ get_weak(), &TerminalPage::_SetTaskbarProgressHandler });
|
||||
|
||||
// TODO GH#3327: Once we support colorizing the NewTab button based on
|
||||
// the color of the tab, we'll want to make sure to call
|
||||
|
@ -1101,14 +1161,33 @@ namespace winrt::TerminalApp::implementation
|
|||
// Arguments:
|
||||
// - direction: The direction to move the focus in.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalPage::_MoveFocus(const FocusDirection& direction)
|
||||
// - Whether changing the focus succeeded. This allows a keychord to propagate
|
||||
// to the terminal when no other panes are present (GH#6219)
|
||||
bool TerminalPage::_MoveFocus(const FocusDirection& direction)
|
||||
{
|
||||
if (const auto terminalTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
_UnZoomIfNeeded();
|
||||
terminalTab->NavigateFocus(direction);
|
||||
return terminalTab->NavigateFocus(direction);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Attempt to swap the positions of the focused pane with another pane.
|
||||
// See Pane::SwapPane for details.
|
||||
// Arguments:
|
||||
// - direction: The direction to move the focused pane in.
|
||||
// Return Value:
|
||||
// - true if panes were swapped.
|
||||
bool TerminalPage::_SwapPane(const FocusDirection& direction)
|
||||
{
|
||||
if (const auto terminalTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
_UnZoomIfNeeded();
|
||||
return terminalTab->SwapPane(direction);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
TermControl TerminalPage::_GetActiveControl()
|
||||
|
@ -1170,6 +1249,56 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Moves the currently active pane on the currently active tab to the
|
||||
// specified tab. If the tab index is greater than the number of
|
||||
// tabs, then a new tab will be created for the pane. Similarly, if a pane
|
||||
// is the last remaining pane on a tab, that tab will be closed upon moving.
|
||||
// - No move will occur if the tabIdx is the same as the current tab, or if
|
||||
// the specified tab is not a host of terminals (such as the settings tab).
|
||||
// Arguments:
|
||||
// - tabIdx: The target tab index.
|
||||
// Return Value:
|
||||
// - true if the pane was successfully moved to the new tab.
|
||||
bool TerminalPage::_MovePane(const uint32_t tabIdx)
|
||||
{
|
||||
auto focusedTab{ _GetFocusedTabImpl() };
|
||||
|
||||
if (!focusedTab)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we are trying to move from the current tab to the current tab do nothing.
|
||||
if (_GetFocusedTabIndex() == tabIdx)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Moving the pane from the current tab might close it, so get the next
|
||||
// tab before its index changes.
|
||||
if (_tabs.Size() > tabIdx)
|
||||
{
|
||||
auto targetTab = _GetTerminalTabImpl(_tabs.GetAt(tabIdx));
|
||||
// if the selected tab is not a host of terminals (e.g. settings)
|
||||
// don't attempt to add a pane to it.
|
||||
if (!targetTab)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto pane = focusedTab->DetachPane();
|
||||
targetTab->AttachPane(pane);
|
||||
_SetFocusedTab(*targetTab);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto pane = focusedTab->DetachPane();
|
||||
_CreateNewTabFromPane(pane);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Split the focused pane either horizontally or vertically, and place the
|
||||
// given TermControl into the newly created pane.
|
||||
|
@ -1200,26 +1329,50 @@ namespace winrt::TerminalApp::implementation
|
|||
return;
|
||||
}
|
||||
|
||||
_SplitPane(*focusedTab, splitType, splitMode, splitSize, newTerminalArgs);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Split the focused pane of the given tab, either horizontally or vertically, and place the
|
||||
// given TermControl into the newly created pane.
|
||||
// - If splitType == SplitState::None, this method does nothing.
|
||||
// Arguments:
|
||||
// - tab: The tab that is going to be split.
|
||||
// - splitType: one value from the TerminalApp::SplitState enum, indicating how the
|
||||
// new pane should be split from its parent.
|
||||
// - splitMode: value from TerminalApp::SplitType enum, indicating the profile to be used in the newly split pane.
|
||||
// - newTerminalArgs: An object that may contain a blob of parameters to
|
||||
// control which profile is created and with possible other
|
||||
// configurations. See CascadiaSettings::BuildSettings for more details.
|
||||
void TerminalPage::_SplitPane(TerminalTab& tab,
|
||||
const SplitState splitType,
|
||||
const SplitType splitMode,
|
||||
const float splitSize,
|
||||
const NewTerminalArgs& newTerminalArgs)
|
||||
{
|
||||
// Do nothing if we're requesting no split.
|
||||
if (splitType == SplitState::None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
TerminalSettingsCreateResult controlSettings{ nullptr };
|
||||
GUID realGuid;
|
||||
bool profileFound = false;
|
||||
Profile profile{ nullptr };
|
||||
|
||||
if (splitMode == SplitType::Duplicate)
|
||||
{
|
||||
std::optional<GUID> current_guid = focusedTab->GetFocusedProfile();
|
||||
if (current_guid)
|
||||
profile = tab.GetFocusedProfile();
|
||||
if (profile)
|
||||
{
|
||||
profileFound = true;
|
||||
controlSettings = TerminalSettings::CreateWithProfileByID(_settings, current_guid.value(), *_bindings);
|
||||
const auto workingDirectory = focusedTab->GetActiveTerminalControl().WorkingDirectory();
|
||||
controlSettings = TerminalSettings::CreateWithProfile(_settings, profile, *_bindings);
|
||||
const auto workingDirectory = tab.GetActiveTerminalControl().WorkingDirectory();
|
||||
const auto validWorkingDirectory = !workingDirectory.empty();
|
||||
if (validWorkingDirectory)
|
||||
{
|
||||
controlSettings.DefaultSettings().StartingDirectory(workingDirectory);
|
||||
}
|
||||
realGuid = current_guid.value();
|
||||
}
|
||||
// TODO: GH#5047 - In the future, we should get the Profile of
|
||||
// the focused pane, and use that to build a new instance of the
|
||||
|
@ -1234,13 +1387,13 @@ namespace winrt::TerminalApp::implementation
|
|||
// connection without keeping an instance of the original Profile
|
||||
// object around.
|
||||
}
|
||||
if (!profileFound)
|
||||
if (!profile)
|
||||
{
|
||||
realGuid = _settings.GetProfileForArgs(newTerminalArgs);
|
||||
profile = _settings.GetProfileForArgs(newTerminalArgs);
|
||||
controlSettings = TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings);
|
||||
}
|
||||
|
||||
const auto controlConnection = _CreateConnectionFromSettings(realGuid, controlSettings.DefaultSettings());
|
||||
const auto controlConnection = _CreateConnectionFromSettings(profile, controlSettings.DefaultSettings());
|
||||
|
||||
const float contentWidth = ::base::saturated_cast<float>(_tabContent.ActualWidth());
|
||||
const float contentHeight = ::base::saturated_cast<float>(_tabContent.ActualHeight());
|
||||
|
@ -1249,10 +1402,10 @@ namespace winrt::TerminalApp::implementation
|
|||
auto realSplitType = splitType;
|
||||
if (realSplitType == SplitState::Automatic)
|
||||
{
|
||||
realSplitType = focusedTab->PreCalculateAutoSplit(availableSpace);
|
||||
realSplitType = tab.PreCalculateAutoSplit(availableSpace);
|
||||
}
|
||||
|
||||
const auto canSplit = focusedTab->PreCalculateCanSplit(realSplitType, splitSize, availableSpace);
|
||||
const auto canSplit = tab.PreCalculateCanSplit(realSplitType, splitSize, availableSpace);
|
||||
if (!canSplit)
|
||||
{
|
||||
return;
|
||||
|
@ -1261,15 +1414,38 @@ namespace winrt::TerminalApp::implementation
|
|||
auto newControl = _InitControl(controlSettings, controlConnection);
|
||||
|
||||
// Hookup our event handlers to the new terminal
|
||||
_RegisterTerminalEvents(newControl, *focusedTab);
|
||||
_RegisterTerminalEvents(newControl);
|
||||
|
||||
_UnZoomIfNeeded();
|
||||
|
||||
focusedTab->SplitPane(realSplitType, splitSize, realGuid, newControl);
|
||||
tab.SplitPane(realSplitType, splitSize, profile, newControl);
|
||||
|
||||
// After GH#6586, the control will no longer focus itself
|
||||
// automatically when it's finished being laid out. Manually focus
|
||||
// the control here instead.
|
||||
if (_startupState == StartupState::Initialized)
|
||||
{
|
||||
_GetActiveControl().Focus(FocusState::Programmatic);
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Switches the split orientation of the currently focused pane.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalPage::_ToggleSplitOrientation()
|
||||
{
|
||||
if (const auto terminalTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
_UnZoomIfNeeded();
|
||||
terminalTab->ToggleSplitOrientation();
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Attempt to move a separator between panes, as to resize each child on
|
||||
// either size of the separator. See Pane::ResizePane for details.
|
||||
|
@ -1833,40 +2009,56 @@ namespace winrt::TerminalApp::implementation
|
|||
_HookupKeyBindings(_settings.ActionMap());
|
||||
|
||||
// Refresh UI elements
|
||||
auto profiles = _settings.ActiveProfiles();
|
||||
for (const auto& profile : profiles)
|
||||
|
||||
// Mapping by GUID isn't _excellent_ because the defaults profile doesn't have a stable GUID; however,
|
||||
// when we stabilize its guid this will become fully safe.
|
||||
std::unordered_map<winrt::guid, std::pair<Profile, TerminalSettingsCreateResult>> profileGuidSettingsMap;
|
||||
const auto profileDefaults{ _settings.ProfileDefaults() };
|
||||
const auto allProfiles{ _settings.AllProfiles() };
|
||||
|
||||
profileGuidSettingsMap.reserve(allProfiles.Size() + 1);
|
||||
|
||||
// Include the Defaults profile for consideration
|
||||
profileGuidSettingsMap.insert_or_assign(profileDefaults.Guid(), std::pair{ profileDefaults, nullptr });
|
||||
for (const auto& newProfile : allProfiles)
|
||||
{
|
||||
const auto profileGuid = profile.Guid();
|
||||
|
||||
try
|
||||
{
|
||||
// This can throw an exception if the profileGuid does
|
||||
// not belong to an actual profile in the list of profiles.
|
||||
auto settings{ TerminalSettings::CreateWithProfileByID(_settings, profileGuid, *_bindings) };
|
||||
|
||||
for (auto tab : _tabs)
|
||||
{
|
||||
if (auto terminalTab = _GetTerminalTabImpl(tab))
|
||||
{
|
||||
terminalTab->UpdateSettings(settings, profileGuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
// Avoid creating a TerminalSettings right now. They're not totally cheap, and we suspect that users with many
|
||||
// panes may not be using all of their profiles at the same time. Lazy evaluation is king!
|
||||
profileGuidSettingsMap.insert_or_assign(newProfile.Guid(), std::pair{ newProfile, nullptr });
|
||||
}
|
||||
|
||||
// GH#2455: If there are any panes with controls that had been
|
||||
// initialized with a Profile that no longer exists in our list of
|
||||
// profiles, we'll leave it unmodified. The profile doesn't exist
|
||||
// anymore, so we can't possibly update its settings.
|
||||
|
||||
// Update the icon of the tab for the currently focused profile in that tab.
|
||||
// Only do this for TerminalTabs. Other types of tabs won't have multiple panes
|
||||
// and profiles so the Title and Icon will be set once and only once on init.
|
||||
for (auto tab : _tabs)
|
||||
for (const auto& tab : _tabs)
|
||||
{
|
||||
if (auto terminalTab = _GetTerminalTabImpl(tab))
|
||||
if (auto terminalTab{ _GetTerminalTabImpl(tab) })
|
||||
{
|
||||
terminalTab->UpdateSettings();
|
||||
|
||||
// Manually enumerate the panes in each tab; this will let us recycle TerminalSettings
|
||||
// objects but only have to iterate one time.
|
||||
terminalTab->GetRootPane()->WalkTree([&](auto&& pane) {
|
||||
if (const auto profile{ pane->GetProfile() })
|
||||
{
|
||||
const auto found{ profileGuidSettingsMap.find(profile.Guid()) };
|
||||
// GH#2455: If there are any panes with controls that had been
|
||||
// initialized with a Profile that no longer exists in our list of
|
||||
// profiles, we'll leave it unmodified. The profile doesn't exist
|
||||
// anymore, so we can't possibly update its settings.
|
||||
if (found != profileGuidSettingsMap.cend())
|
||||
{
|
||||
auto& pair{ found->second };
|
||||
if (!pair.second)
|
||||
{
|
||||
pair.second = TerminalSettings::CreateWithProfile(_settings, pair.first, *_bindings);
|
||||
}
|
||||
pane->UpdateSettings(pair.second, pair.first);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// Update the icon of the tab for the currently focused profile in that tab.
|
||||
// Only do this for TerminalTabs. Other types of tabs won't have multiple panes
|
||||
// and profiles so the Title and Icon will be set once and only once on init.
|
||||
_UpdateTabIcon(*terminalTab);
|
||||
|
||||
// Force the TerminalTab to re-grab its currently active control's title.
|
||||
|
@ -2017,29 +2209,35 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
|
||||
// Method Description:
|
||||
// - Gets the taskbar state value from the last active control
|
||||
// - Get the combined taskbar state for the page. This is the combination of
|
||||
// all the states of all the tabs, which are themselves a combination of
|
||||
// all their panes. Taskbar states are given a priority based on the rules
|
||||
// in:
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-itaskbarlist3-setprogressstate
|
||||
// under "How the Taskbar Button Chooses the Progress Indicator for a Group"
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - The taskbar state of the last active control
|
||||
size_t TerminalPage::GetLastActiveControlTaskbarState()
|
||||
// - A TaskbarState object representing the combined taskbar state and
|
||||
// progress percentage of all our tabs.
|
||||
winrt::TerminalApp::TaskbarState TerminalPage::TaskbarState() const
|
||||
{
|
||||
if (auto control{ _GetActiveControl() })
|
||||
{
|
||||
return gsl::narrow_cast<size_t>(control.TaskbarState());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
auto state{ winrt::make<winrt::TerminalApp::implementation::TaskbarState>() };
|
||||
|
||||
// Method Description:
|
||||
// - Gets the taskbar progress value from the last active control
|
||||
// Return Value:
|
||||
// - The taskbar progress of the last active control
|
||||
size_t TerminalPage::GetLastActiveControlTaskbarProgress()
|
||||
{
|
||||
if (auto control{ _GetActiveControl() })
|
||||
for (const auto& tab : _tabs)
|
||||
{
|
||||
return gsl::narrow_cast<size_t>(control.TaskbarProgress());
|
||||
if (auto tabImpl{ _GetTerminalTabImpl(tab) })
|
||||
{
|
||||
auto tabState{ tabImpl->GetCombinedTaskbarState() };
|
||||
// lowest priority wins
|
||||
if (tabState.Priority() < state.Priority())
|
||||
{
|
||||
state = tabState;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -2325,13 +2523,38 @@ namespace winrt::TerminalApp::implementation
|
|||
return _isAlwaysOnTop;
|
||||
}
|
||||
|
||||
void TerminalPage::_OnNewConnection(winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection connection)
|
||||
HRESULT TerminalPage::_OnNewConnection(winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection connection)
|
||||
{
|
||||
// TODO: GH 9458 will give us more context so we can try to choose a better profile.
|
||||
_OpenNewTab(nullptr, connection);
|
||||
// We need to be on the UI thread in order for _OpenNewTab to run successfully.
|
||||
// HasThreadAccess will return true if we're currently on a UI thread and false otherwise.
|
||||
// When we're on a COM thread, we'll need to dispatch the calls to the UI thread
|
||||
// and wait on it hence the locking mechanism.
|
||||
if (Dispatcher().HasThreadAccess())
|
||||
{
|
||||
// TODO: GH 9458 will give us more context so we can try to choose a better profile.
|
||||
auto hr = _OpenNewTab(nullptr, connection);
|
||||
|
||||
// Request a summon of this window to the foreground
|
||||
_SummonWindowRequestedHandlers(*this, nullptr);
|
||||
// Request a summon of this window to the foreground
|
||||
_SummonWindowRequestedHandlers(*this, nullptr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
else
|
||||
{
|
||||
til::latch latch{ 1 };
|
||||
HRESULT finalVal = S_OK;
|
||||
|
||||
Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [&]() {
|
||||
finalVal = _OpenNewTab(nullptr, connection);
|
||||
|
||||
_SummonWindowRequestedHandlers(*this, nullptr);
|
||||
|
||||
latch.count_down();
|
||||
});
|
||||
|
||||
latch.wait();
|
||||
return finalVal;
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
|
|
@ -58,6 +58,8 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
void Create();
|
||||
|
||||
winrt::fire_and_forget NewTerminalByDrop(winrt::Windows::UI::Xaml::DragEventArgs& e);
|
||||
|
||||
hstring Title();
|
||||
|
||||
void TitlebarClicked();
|
||||
|
@ -83,8 +85,7 @@ namespace winrt::TerminalApp::implementation
|
|||
winrt::TerminalApp::IDialogPresenter DialogPresenter() const;
|
||||
void DialogPresenter(winrt::TerminalApp::IDialogPresenter dialogPresenter);
|
||||
|
||||
size_t GetLastActiveControlTaskbarState();
|
||||
size_t GetLastActiveControlTaskbarProgress();
|
||||
winrt::TerminalApp::TaskbarState TaskbarState() const;
|
||||
|
||||
void ShowKeyboardServiceWarning();
|
||||
winrt::hstring KeyboardServiceDisabledText();
|
||||
|
@ -186,12 +187,15 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
void _CreateNewTabFlyout();
|
||||
void _OpenNewTabDropdown();
|
||||
void _OpenNewTab(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr);
|
||||
void _CreateNewTabFromSettings(GUID profileGuid, const Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr);
|
||||
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(GUID profileGuid, Microsoft::Terminal::Settings::Model::TerminalSettings settings);
|
||||
HRESULT _OpenNewTab(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr);
|
||||
void _CreateNewTabFromPane(std::shared_ptr<Pane> pane);
|
||||
void _CreateNewTabWithProfileAndSettings(const Microsoft::Terminal::Settings::Model::Profile& profile, const Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr);
|
||||
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(Microsoft::Terminal::Settings::Model::Profile profile, Microsoft::Terminal::Settings::Model::TerminalSettings settings);
|
||||
|
||||
winrt::fire_and_forget _OpenNewWindow(const bool elevate, const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs);
|
||||
|
||||
void _OpenNewTerminal(const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs);
|
||||
|
||||
bool _displayingCloseDialog{ false };
|
||||
void _SettingsButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
|
||||
void _CommandPaletteButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
|
||||
|
@ -215,12 +219,16 @@ namespace winrt::TerminalApp::implementation
|
|||
void _DuplicateFocusedTab();
|
||||
void _DuplicateTab(const TerminalTab& tab);
|
||||
|
||||
void _SplitTab(TerminalTab& tab);
|
||||
|
||||
winrt::Windows::Foundation::IAsyncAction _HandleCloseTabRequested(winrt::TerminalApp::TabBase tab);
|
||||
void _CloseTabAtIndex(uint32_t index);
|
||||
void _RemoveTab(const winrt::TerminalApp::TabBase& tab);
|
||||
winrt::fire_and_forget _RemoveTabs(const std::vector<winrt::TerminalApp::TabBase> tabs);
|
||||
|
||||
void _RegisterTerminalEvents(Microsoft::Terminal::Control::TermControl term, TerminalTab& hostingTab);
|
||||
void _InitializeTab(winrt::com_ptr<TerminalTab> newTabImpl);
|
||||
void _RegisterTerminalEvents(Microsoft::Terminal::Control::TermControl term);
|
||||
void _RegisterTabEvents(TerminalTab& hostingTab);
|
||||
|
||||
void _DismissTabContextMenus();
|
||||
void _FocusCurrentTab(const bool focusAlways);
|
||||
|
@ -229,8 +237,10 @@ namespace winrt::TerminalApp::implementation
|
|||
void _ResizeTabContent(const winrt::Windows::Foundation::Size& newSize);
|
||||
|
||||
void _SelectNextTab(const bool bMoveRight, const Windows::Foundation::IReference<Microsoft::Terminal::Settings::Model::TabSwitcherMode>& customTabSwitcherMode);
|
||||
bool _SelectTab(const uint32_t tabIndex);
|
||||
void _MoveFocus(const Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
bool _SelectTab(uint32_t tabIndex);
|
||||
bool _MoveFocus(const Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
bool _SwapPane(const Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
bool _MovePane(const uint32_t tabIdx);
|
||||
|
||||
winrt::Microsoft::Terminal::Control::TermControl _GetActiveControl();
|
||||
std::optional<uint32_t> _GetFocusedTabIndex() const noexcept;
|
||||
|
@ -249,7 +259,13 @@ namespace winrt::TerminalApp::implementation
|
|||
const Microsoft::Terminal::Settings::Model::SplitType splitMode = Microsoft::Terminal::Settings::Model::SplitType::Manual,
|
||||
const float splitSize = 0.5f,
|
||||
const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs = nullptr);
|
||||
void _SplitPane(TerminalTab& tab,
|
||||
const Microsoft::Terminal::Settings::Model::SplitState splitType,
|
||||
const Microsoft::Terminal::Settings::Model::SplitType splitMode = Microsoft::Terminal::Settings::Model::SplitType::Manual,
|
||||
const float splitSize = 0.5f,
|
||||
const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs = nullptr);
|
||||
void _ResizePane(const Microsoft::Terminal::Settings::Model::ResizeDirection& direction);
|
||||
void _ToggleSplitOrientation();
|
||||
|
||||
void _ScrollPage(ScrollDirection scrollDirection);
|
||||
void _ScrollToBufferEdge(ScrollDirection scrollDirection);
|
||||
|
@ -331,7 +347,7 @@ namespace winrt::TerminalApp::implementation
|
|||
winrt::Microsoft::Terminal::Settings::Model::Command _lastPreviewedCommand{ nullptr };
|
||||
winrt::Microsoft::Terminal::Settings::Model::TerminalSettings _originalSettings{ nullptr };
|
||||
|
||||
void _OnNewConnection(winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection connection);
|
||||
HRESULT _OnNewConnection(winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection connection);
|
||||
void _HandleToggleInboundPty(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
|
||||
|
||||
void _WindowRenamerActionClick(const IInspectable& sender, const IInspectable& eventArgs);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
import "TaskbarState.idl";
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
|
@ -42,8 +43,7 @@ namespace TerminalApp
|
|||
void ShowKeyboardServiceWarning();
|
||||
String KeyboardServiceDisabledText { get; };
|
||||
|
||||
UInt64 GetLastActiveControlTaskbarState();
|
||||
UInt64 GetLastActiveControlTaskbarProgress();
|
||||
TaskbarState TaskbarState{ get; };
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, String> TitleChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, LastTabClosedEventArgs> LastTabClosed;
|
||||
|
|
|
@ -25,19 +25,66 @@ namespace winrt
|
|||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
TerminalTab::TerminalTab(const GUID& profile, const TermControl& control)
|
||||
TerminalTab::TerminalTab(const Profile& profile, const TermControl& control)
|
||||
{
|
||||
_rootPane = std::make_shared<Pane>(profile, control, true);
|
||||
|
||||
_rootPane->Id(_nextPaneId);
|
||||
_activePane = _rootPane;
|
||||
_mruPanes.insert(_mruPanes.begin(), _nextPaneId);
|
||||
++_nextPaneId;
|
||||
|
||||
_rootPane->Closed([=](auto&& /*s*/, auto&& /*e*/) {
|
||||
_Setup();
|
||||
}
|
||||
|
||||
TerminalTab::TerminalTab(std::shared_ptr<Pane> rootPane)
|
||||
{
|
||||
_rootPane = rootPane;
|
||||
_activePane = nullptr;
|
||||
|
||||
auto firstId = _nextPaneId;
|
||||
|
||||
_rootPane->WalkTree([&](std::shared_ptr<Pane> pane) {
|
||||
// update the IDs on each pane
|
||||
if (pane->_IsLeaf())
|
||||
{
|
||||
pane->Id(_nextPaneId);
|
||||
_nextPaneId++;
|
||||
}
|
||||
// Try to find the pane marked active (if it exists)
|
||||
if (pane->_lastActive)
|
||||
{
|
||||
_activePane = pane;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
// In case none of the panes were already marked as the focus, just
|
||||
// focus the first one.
|
||||
if (_activePane == nullptr)
|
||||
{
|
||||
_rootPane->FocusPane(firstId);
|
||||
_activePane = _rootPane->GetActivePane();
|
||||
}
|
||||
// Set the active control
|
||||
_mruPanes.insert(_mruPanes.begin(), _activePane->Id().value());
|
||||
|
||||
_Setup();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Shared setup for the constructors. Assumed that _rootPane has been set.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalTab::_Setup()
|
||||
{
|
||||
_rootClosedToken = _rootPane->Closed([=](auto&& /*s*/, auto&& /*e*/) {
|
||||
_ClosedHandlers(nullptr, nullptr);
|
||||
});
|
||||
|
||||
_activePane = _rootPane;
|
||||
Content(_rootPane->GetRootElement());
|
||||
|
||||
_MakeTabViewItem();
|
||||
|
@ -144,19 +191,31 @@ namespace winrt::TerminalApp::implementation
|
|||
// that was last focused.
|
||||
TermControl TerminalTab::GetActiveTerminalControl() const
|
||||
{
|
||||
return _activePane->GetTerminalControl();
|
||||
if (_activePane)
|
||||
{
|
||||
return _activePane->GetTerminalControl();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called after construction of a Tab object to bind event handlers to its
|
||||
// associated Pane and TermControl object
|
||||
// associated Pane and TermControl objects
|
||||
// Arguments:
|
||||
// - control: reference to the TermControl object to bind event to
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalTab::Initialize(const TermControl& control)
|
||||
void TerminalTab::Initialize()
|
||||
{
|
||||
_BindEventHandlers(control);
|
||||
_rootPane->WalkTree([&](std::shared_ptr<Pane> pane) {
|
||||
// Attach event handlers to each new pane
|
||||
_AttachEventHandlersToPane(pane);
|
||||
if (auto control = pane->GetTerminalControl())
|
||||
{
|
||||
_AttachEventHandlersToControl(pane->Id().value(), control);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -177,10 +236,9 @@ namespace winrt::TerminalApp::implementation
|
|||
{
|
||||
lastFocusedControl.Focus(_focusState);
|
||||
|
||||
// Update our own progress state, and fire an event signaling
|
||||
// Update our own progress state. This will fire an event signaling
|
||||
// that our taskbar progress changed.
|
||||
_UpdateProgressState();
|
||||
_TaskbarProgressChangedHandlers(lastFocusedControl, nullptr);
|
||||
}
|
||||
// When we gain focus, remove the bell indicator if it is active
|
||||
if (_tabStatus.BellIndicator())
|
||||
|
@ -199,35 +257,19 @@ namespace winrt::TerminalApp::implementation
|
|||
// Return Value:
|
||||
// - nullopt if no children of this tab were the last control to be
|
||||
// focused, else the GUID of the profile of the last control to be focused
|
||||
std::optional<GUID> TerminalTab::GetFocusedProfile() const noexcept
|
||||
Profile TerminalTab::GetFocusedProfile() const noexcept
|
||||
{
|
||||
return _activePane->GetFocusedProfile();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called after construction of a Tab object to bind event handlers to its
|
||||
// associated Pane and TermControl object
|
||||
// Arguments:
|
||||
// - control: reference to the TermControl object to bind event to
|
||||
// - Attempts to update the settings that apply to this tab.
|
||||
// - Panes are handled elsewhere, by somebody who can establish broader knowledge
|
||||
// of the settings that apply to all tabs.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalTab::_BindEventHandlers(const TermControl& control) noexcept
|
||||
void TerminalTab::UpdateSettings()
|
||||
{
|
||||
_AttachEventHandlersToPane(_rootPane);
|
||||
_AttachEventHandlersToControl(control);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Attempts to update the settings of this tab's tree of panes.
|
||||
// Arguments:
|
||||
// - settings: The new TerminalSettingsCreateResult to apply to any matching controls
|
||||
// - profile: The GUID of the profile these settings should apply to.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalTab::UpdateSettings(const TerminalSettingsCreateResult& settings, const GUID& profile)
|
||||
{
|
||||
_rootPane->UpdateSettings(settings, profile);
|
||||
|
||||
// The tabWidthMode may have changed, update the header control accordingly
|
||||
_UpdateHeaderControlMaxWidth();
|
||||
}
|
||||
|
@ -406,7 +448,7 @@ namespace winrt::TerminalApp::implementation
|
|||
// - <none>
|
||||
void TerminalTab::SplitPane(SplitState splitType,
|
||||
const float splitSize,
|
||||
const GUID& profile,
|
||||
const Profile& profile,
|
||||
TermControl& control)
|
||||
{
|
||||
// Make sure to take the ID before calling Split() - Split() will clear out the active pane's ID
|
||||
|
@ -426,10 +468,10 @@ namespace winrt::TerminalApp::implementation
|
|||
++_nextPaneId;
|
||||
}
|
||||
_activePane = first;
|
||||
_AttachEventHandlersToControl(control);
|
||||
|
||||
// Add a event handlers to the new panes' GotFocus event. When the pane
|
||||
// gains focus, we'll mark it as the new active pane.
|
||||
_AttachEventHandlersToControl(second->Id().value(), control);
|
||||
_AttachEventHandlersToPane(first);
|
||||
_AttachEventHandlersToPane(second);
|
||||
|
||||
|
@ -440,6 +482,127 @@ namespace winrt::TerminalApp::implementation
|
|||
_UpdateActivePane(second);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Removes the currently active pane from this tab. If that was the only
|
||||
// remaining pane, then the entire tab is closed as well.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - The removed pane, if the remove succeeded.
|
||||
std::shared_ptr<Pane> TerminalTab::DetachPane()
|
||||
{
|
||||
// if we only have one pane, remove it entirely
|
||||
// and close this tab
|
||||
if (_rootPane == _activePane)
|
||||
{
|
||||
return DetachRoot();
|
||||
}
|
||||
|
||||
// Attempt to remove the active pane from the tree
|
||||
if (const auto pane = _rootPane->DetachPane(_activePane))
|
||||
{
|
||||
// Just make sure that the remaining pane is marked active
|
||||
_UpdateActivePane(_rootPane->GetActivePane());
|
||||
|
||||
return pane;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Closes this tab and returns the root pane to be used elsewhere.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - The root pane.
|
||||
std::shared_ptr<Pane> TerminalTab::DetachRoot()
|
||||
{
|
||||
// remove the closed event handler since we are closing the tab
|
||||
// manually.
|
||||
_rootPane->Closed(_rootClosedToken);
|
||||
auto p = _rootPane;
|
||||
p->WalkTree([](auto pane) {
|
||||
pane->_PaneDetachedHandlers(pane);
|
||||
return false;
|
||||
});
|
||||
|
||||
// Clean up references and close the tab
|
||||
_rootPane = nullptr;
|
||||
_activePane = nullptr;
|
||||
Content(nullptr);
|
||||
_ClosedHandlers(nullptr, nullptr);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Add an arbitrary pane to this tab. This will be added as a split on the
|
||||
// currently active pane.
|
||||
// Arguments:
|
||||
// - pane: The pane to add.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalTab::AttachPane(std::shared_ptr<Pane> pane)
|
||||
{
|
||||
// Add the new event handlers to the new pane(s)
|
||||
// and update their ids.
|
||||
pane->WalkTree([&](auto p) {
|
||||
_AttachEventHandlersToPane(p);
|
||||
if (p->_IsLeaf())
|
||||
{
|
||||
p->Id(_nextPaneId);
|
||||
_nextPaneId++;
|
||||
}
|
||||
if (auto control = p->GetTerminalControl())
|
||||
{
|
||||
_AttachEventHandlersToControl(p->Id().value(), control);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// pass the old id to the new child
|
||||
const auto previousId = _activePane->Id();
|
||||
|
||||
// Add the new pane as an automatic split on the active pane.
|
||||
auto first = _activePane->AttachPane(pane, SplitState::Automatic);
|
||||
|
||||
// under current assumptions this condition should always be true.
|
||||
if (previousId)
|
||||
{
|
||||
first->Id(previousId.value());
|
||||
}
|
||||
else
|
||||
{
|
||||
first->Id(_nextPaneId);
|
||||
++_nextPaneId;
|
||||
}
|
||||
|
||||
// Update with event handlers on the new child.
|
||||
_activePane = first;
|
||||
_AttachEventHandlersToPane(first);
|
||||
|
||||
// Make sure that we have the right pane set as the active pane
|
||||
pane->WalkTree([&](auto p) {
|
||||
if (p->_lastActive)
|
||||
{
|
||||
_UpdateActivePane(p);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Find the currently active pane, and then switch the split direction of
|
||||
// its parent. E.g. switch from Horizontal to Vertical.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalTab::ToggleSplitOrientation()
|
||||
{
|
||||
_rootPane->ToggleSplitOrientation();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - See Pane::CalcSnappedDimension
|
||||
float TerminalTab::CalcSnappedDimension(const bool widthOrHeight, const float dimension) const
|
||||
|
@ -481,22 +644,68 @@ namespace winrt::TerminalApp::implementation
|
|||
// Arguments:
|
||||
// - direction: The direction to move the focus in.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalTab::NavigateFocus(const FocusDirection& direction)
|
||||
// - Whether changing the focus succeeded. This allows a keychord to propagate
|
||||
// to the terminal when no other panes are present (GH#6219)
|
||||
bool TerminalTab::NavigateFocus(const FocusDirection& direction)
|
||||
{
|
||||
if (direction == FocusDirection::Previous)
|
||||
{
|
||||
if (_mruPanes.size() < 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// To get to the previous pane, get the id of the previous pane and focus to that
|
||||
_rootPane->FocusPane(_mruPanes.at(1));
|
||||
return _rootPane->FocusPane(_mruPanes.at(1));
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: This _must_ be called on the root pane, so that it can propagate
|
||||
// throughout the entire tree.
|
||||
_rootPane->NavigateFocus(direction);
|
||||
if (auto newFocus = _rootPane->NavigateDirection(_activePane, direction))
|
||||
{
|
||||
return _rootPane->FocusPane(newFocus);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Attempts to swap the location of the focused pane with another pane
|
||||
// according to direction. When there are multiple adjacent panes it will
|
||||
// select the first one (top-left-most).
|
||||
// Arguments:
|
||||
// - direction: The direction to move the pane in.
|
||||
// Return Value:
|
||||
// - true if two panes were swapped.
|
||||
bool TerminalTab::SwapPane(const FocusDirection& direction)
|
||||
{
|
||||
if (direction == FocusDirection::Previous)
|
||||
{
|
||||
if (_mruPanes.size() < 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (auto lastPane = _rootPane->FindPane(_mruPanes.at(1)))
|
||||
{
|
||||
return _rootPane->SwapPanes(_activePane, lastPane);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: This _must_ be called on the root pane, so that it can propagate
|
||||
// throughout the entire tree.
|
||||
if (auto neighbor = _rootPane->NavigateDirection(_activePane, direction))
|
||||
{
|
||||
return _rootPane->SwapPanes(_activePane, neighbor);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TerminalTab::FocusPane(const uint32_t id)
|
||||
{
|
||||
return _rootPane->FocusPane(id);
|
||||
|
@ -506,7 +715,10 @@ namespace winrt::TerminalApp::implementation
|
|||
// - Prepares this tab for being removed from the UI hierarchy by shutting down all active connections.
|
||||
void TerminalTab::Shutdown()
|
||||
{
|
||||
_rootPane->Shutdown();
|
||||
if (_rootPane)
|
||||
{
|
||||
_rootPane->Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -551,6 +763,34 @@ namespace winrt::TerminalApp::implementation
|
|||
_headerControl.BeginRename();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Removes any event handlers set by the tab on the given pane's control.
|
||||
// The pane's ID is the most stable identifier for a given control, because
|
||||
// the control itself doesn't have a particular ID and its pointer is
|
||||
// unstable since it is moved when panes split.
|
||||
// Arguments:
|
||||
// - paneId: The ID of the pane that contains the given control.
|
||||
// - control: the control to remove events from.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalTab::_DetachEventHandlersFromControl(const uint32_t paneId, const TermControl& control)
|
||||
{
|
||||
auto it = _controlEvents.find(paneId);
|
||||
if (it != _controlEvents.end())
|
||||
{
|
||||
auto& events = it->second;
|
||||
|
||||
control.TitleChanged(events.titleToken);
|
||||
control.FontSizeChanged(events.fontToken);
|
||||
control.TabColorChanged(events.colorToken);
|
||||
control.SetTaskbarProgress(events.taskbarToken);
|
||||
control.ReadOnlyChanged(events.readOnlyToken);
|
||||
control.FocusFollowMouseRequested(events.focusToken);
|
||||
|
||||
_controlEvents.erase(paneId);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Register any event handlers that we may need with the given TermControl.
|
||||
// This should be called on each and every TermControl that we add to the tree
|
||||
|
@ -558,15 +798,17 @@ namespace winrt::TerminalApp::implementation
|
|||
// * notify us when the control's title changed, so we can update our own
|
||||
// title (if necessary)
|
||||
// Arguments:
|
||||
// - paneId: the ID of the pane that this control belongs to.
|
||||
// - control: the TermControl to add events to.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalTab::_AttachEventHandlersToControl(const TermControl& control)
|
||||
void TerminalTab::_AttachEventHandlersToControl(const uint32_t paneId, const TermControl& control)
|
||||
{
|
||||
auto weakThis{ get_weak() };
|
||||
auto dispatcher = TabViewItem().Dispatcher();
|
||||
ControlEventTokens events{};
|
||||
|
||||
control.TitleChanged([weakThis](auto&&, auto&&) {
|
||||
events.titleToken = control.TitleChanged([weakThis](auto&&, auto&&) {
|
||||
// Check if Tab's lifetime has expired
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
|
@ -581,16 +823,16 @@ namespace winrt::TerminalApp::implementation
|
|||
// On the latter event, we tell the root pane to resize itself so that its descendants
|
||||
// (including ourself) can properly snap to character grids. In future, we may also
|
||||
// want to do that on regular font changes.
|
||||
control.FontSizeChanged([this](const int /* fontWidth */,
|
||||
const int /* fontHeight */,
|
||||
const bool isInitialChange) {
|
||||
events.fontToken = control.FontSizeChanged([this](const int /* fontWidth */,
|
||||
const int /* fontHeight */,
|
||||
const bool isInitialChange) {
|
||||
if (isInitialChange)
|
||||
{
|
||||
_rootPane->Relayout();
|
||||
}
|
||||
});
|
||||
|
||||
control.TabColorChanged([weakThis](auto&&, auto&&) {
|
||||
events.colorToken = control.TabColorChanged([weakThis](auto&&, auto&&) {
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
// The control's tabColor changed, but it is not necessarily the
|
||||
|
@ -600,7 +842,7 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
});
|
||||
|
||||
control.SetTaskbarProgress([dispatcher, weakThis](auto&&, auto &&) -> winrt::fire_and_forget {
|
||||
events.taskbarToken = control.SetTaskbarProgress([dispatcher, weakThis](auto&&, auto &&) -> winrt::fire_and_forget {
|
||||
co_await winrt::resume_foreground(dispatcher);
|
||||
// Check if Tab's lifetime has expired
|
||||
if (auto tab{ weakThis.get() })
|
||||
|
@ -609,14 +851,14 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
});
|
||||
|
||||
control.ReadOnlyChanged([weakThis](auto&&, auto&&) {
|
||||
events.readOnlyToken = control.ReadOnlyChanged([weakThis](auto&&, auto&&) {
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
tab->_RecalculateAndApplyReadOnly();
|
||||
}
|
||||
});
|
||||
|
||||
control.FocusFollowMouseRequested([weakThis](auto&& sender, auto&&) {
|
||||
events.focusToken = control.FocusFollowMouseRequested([weakThis](auto&& sender, auto&&) {
|
||||
if (const auto tab{ weakThis.get() })
|
||||
{
|
||||
if (tab->_focusState != FocusState::Unfocused)
|
||||
|
@ -628,6 +870,31 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
_controlEvents[paneId] = events;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Get the combined taskbar state for the tab. This is the combination of
|
||||
// all the states of all our panes. Taskbar states are given a priority
|
||||
// based on the rules in:
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-itaskbarlist3-setprogressstate
|
||||
// under "How the Taskbar Button Chooses the Progress Indicator for a
|
||||
// Group"
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - A TaskbarState object representing the combined taskbar state and
|
||||
// progress percentage of all our panes.
|
||||
winrt::TerminalApp::TaskbarState TerminalTab::GetCombinedTaskbarState() const
|
||||
{
|
||||
std::vector<winrt::TerminalApp::TaskbarState> states;
|
||||
if (_rootPane)
|
||||
{
|
||||
_rootPane->CollectTaskbarStates(states);
|
||||
}
|
||||
return states.empty() ? winrt::make<winrt::TerminalApp::implementation::TaskbarState>() :
|
||||
*std::min_element(states.begin(), states.end(), TerminalApp::implementation::TaskbarState::ComparePriority);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -645,37 +912,39 @@ namespace winrt::TerminalApp::implementation
|
|||
// - <none>
|
||||
void TerminalTab::_UpdateProgressState()
|
||||
{
|
||||
if (const auto& activeControl{ GetActiveTerminalControl() })
|
||||
{
|
||||
const auto taskbarState = activeControl.TaskbarState();
|
||||
// The progress of the control changed, but not necessarily the progress of the tab.
|
||||
// Set the tab's progress ring to the active pane's progress
|
||||
if (taskbarState > 0)
|
||||
{
|
||||
if (taskbarState == 3)
|
||||
{
|
||||
// 3 is the indeterminate state, set the progress ring as such
|
||||
_tabStatus.IsProgressRingIndeterminate(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// any non-indeterminate state has a value, set the progress ring as such
|
||||
_tabStatus.IsProgressRingIndeterminate(false);
|
||||
const auto state{ GetCombinedTaskbarState() };
|
||||
|
||||
const auto progressValue = gsl::narrow<uint32_t>(activeControl.TaskbarProgress());
|
||||
_tabStatus.ProgressValue(progressValue);
|
||||
}
|
||||
// Hide the tab icon (the progress ring is placed over it)
|
||||
HideIcon(true);
|
||||
_tabStatus.IsProgressRingActive(true);
|
||||
const auto taskbarState = state.State();
|
||||
// The progress of the control changed, but not necessarily the progress of the tab.
|
||||
// Set the tab's progress ring to the active pane's progress
|
||||
if (taskbarState > 0)
|
||||
{
|
||||
if (taskbarState == 3)
|
||||
{
|
||||
// 3 is the indeterminate state, set the progress ring as such
|
||||
_tabStatus.IsProgressRingIndeterminate(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Show the tab icon
|
||||
HideIcon(false);
|
||||
_tabStatus.IsProgressRingActive(false);
|
||||
// any non-indeterminate state has a value, set the progress ring as such
|
||||
_tabStatus.IsProgressRingIndeterminate(false);
|
||||
|
||||
const auto progressValue = gsl::narrow<uint32_t>(state.Progress());
|
||||
_tabStatus.ProgressValue(progressValue);
|
||||
}
|
||||
// Hide the tab icon (the progress ring is placed over it)
|
||||
HideIcon(true);
|
||||
_tabStatus.IsProgressRingActive(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Show the tab icon
|
||||
HideIcon(false);
|
||||
_tabStatus.IsProgressRingActive(false);
|
||||
}
|
||||
|
||||
// fire an event signaling that our taskbar progress changed.
|
||||
_TaskbarProgressChangedHandlers(nullptr, nullptr);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -732,7 +1001,7 @@ namespace winrt::TerminalApp::implementation
|
|||
auto weakThis{ get_weak() };
|
||||
std::weak_ptr<Pane> weakPane{ pane };
|
||||
|
||||
pane->GotFocus([weakThis](std::shared_ptr<Pane> sender) {
|
||||
auto gotFocusToken = pane->GotFocus([weakThis](std::shared_ptr<Pane> sender) {
|
||||
// Do nothing if the Tab's lifetime is expired or pane isn't new.
|
||||
auto tab{ weakThis.get() };
|
||||
|
||||
|
@ -752,7 +1021,7 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
});
|
||||
|
||||
pane->LostFocus([weakThis](std::shared_ptr<Pane> /*sender*/) {
|
||||
auto lostFocusToken = pane->LostFocus([weakThis](std::shared_ptr<Pane> /*sender*/) {
|
||||
// Do nothing if the Tab's lifetime is expired or pane isn't new.
|
||||
auto tab{ weakThis.get() };
|
||||
|
||||
|
@ -766,7 +1035,7 @@ namespace winrt::TerminalApp::implementation
|
|||
// Add a Closed event handler to the Pane. If the pane closes out from
|
||||
// underneath us, and it's zoomed, we want to be able to make sure to
|
||||
// update our state accordingly to un-zoom that pane. See GH#7252.
|
||||
pane->Closed([weakThis, weakPane](auto&& /*s*/, auto && /*e*/) -> winrt::fire_and_forget {
|
||||
auto closedToken = pane->Closed([weakThis, weakPane](auto&& /*s*/, auto && /*e*/) -> winrt::fire_and_forget {
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
if (tab->_zoomedPane)
|
||||
|
@ -791,7 +1060,7 @@ namespace winrt::TerminalApp::implementation
|
|||
});
|
||||
|
||||
// Add a PaneRaiseBell event handler to the Pane
|
||||
pane->PaneRaiseBell([weakThis](auto&& /*s*/, auto&& visual) {
|
||||
auto bellToken = pane->PaneRaiseBell([weakThis](auto&& /*s*/, auto&& visual) {
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
if (visual)
|
||||
|
@ -813,6 +1082,40 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
// box the event token so that we can give a reference to it in the
|
||||
// event handler.
|
||||
auto detachedToken = std::make_shared<winrt::event_token>();
|
||||
// Add a Detached event handler to the Pane to clean up tab state
|
||||
// and other event handlers when a pane is removed from this tab.
|
||||
*detachedToken = pane->Detached([weakThis, weakPane, gotFocusToken, lostFocusToken, closedToken, bellToken, detachedToken](std::shared_ptr<Pane> /*sender*/) {
|
||||
// Make sure we do this at most once
|
||||
if (auto pane{ weakPane.lock() })
|
||||
{
|
||||
pane->Detached(*detachedToken);
|
||||
pane->GotFocus(gotFocusToken);
|
||||
pane->LostFocus(lostFocusToken);
|
||||
pane->Closed(closedToken);
|
||||
pane->PaneRaiseBell(bellToken);
|
||||
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
if (auto control = pane->GetTerminalControl())
|
||||
{
|
||||
tab->_DetachEventHandlersFromControl(pane->Id().value(), control);
|
||||
}
|
||||
|
||||
for (auto i = tab->_mruPanes.begin(); i != tab->_mruPanes.end(); ++i)
|
||||
{
|
||||
if (*i == pane->Id())
|
||||
{
|
||||
tab->_mruPanes.erase(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -891,12 +1194,30 @@ namespace winrt::TerminalApp::implementation
|
|||
duplicateTabMenuItem.Icon(duplicateTabSymbol);
|
||||
}
|
||||
|
||||
Controls::MenuFlyoutItem splitTabMenuItem;
|
||||
{
|
||||
// "Split Tab"
|
||||
Controls::FontIcon splitTabSymbol;
|
||||
splitTabSymbol.FontFamily(Media::FontFamily{ L"Segoe MDL2 Assets" });
|
||||
splitTabSymbol.Glyph(L"\xF246"); // ViewDashboard
|
||||
|
||||
splitTabMenuItem.Click([weakThis](auto&&, auto&&) {
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
tab->_SplitTabRequestedHandlers();
|
||||
}
|
||||
});
|
||||
splitTabMenuItem.Text(RS_(L"SplitTabText"));
|
||||
splitTabMenuItem.Icon(splitTabSymbol);
|
||||
}
|
||||
|
||||
// Build the menu
|
||||
Controls::MenuFlyout contextMenuFlyout;
|
||||
Controls::MenuFlyoutSeparator menuSeparator;
|
||||
contextMenuFlyout.Items().Append(chooseColorMenuItem);
|
||||
contextMenuFlyout.Items().Append(renameTabMenuItem);
|
||||
contextMenuFlyout.Items().Append(duplicateTabMenuItem);
|
||||
contextMenuFlyout.Items().Append(splitTabMenuItem);
|
||||
contextMenuFlyout.Items().Append(menuSeparator);
|
||||
|
||||
// GH#5750 - When the context menu is dismissed with ESC, toss the focus
|
||||
|
@ -1188,6 +1509,7 @@ namespace winrt::TerminalApp::implementation
|
|||
EnterZoom();
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalTab::EnterZoom()
|
||||
{
|
||||
_zoomedPane = _activePane;
|
||||
|
@ -1269,4 +1591,5 @@ namespace winrt::TerminalApp::implementation
|
|||
DEFINE_EVENT(TerminalTab, ColorCleared, _colorCleared, winrt::delegate<>);
|
||||
DEFINE_EVENT(TerminalTab, TabRaiseVisualBell, _TabRaiseVisualBellHandlers, winrt::delegate<>);
|
||||
DEFINE_EVENT(TerminalTab, DuplicateRequested, _DuplicateRequestedHandlers, winrt::delegate<>);
|
||||
DEFINE_EVENT(TerminalTab, SplitTabRequested, _SplitTabRequestedHandlers, winrt::delegate<>);
|
||||
}
|
||||
|
|
|
@ -21,23 +21,29 @@ namespace winrt::TerminalApp::implementation
|
|||
struct TerminalTab : TerminalTabT<TerminalTab, TabBase>
|
||||
{
|
||||
public:
|
||||
TerminalTab(const GUID& profile, const winrt::Microsoft::Terminal::Control::TermControl& control);
|
||||
TerminalTab(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile, const winrt::Microsoft::Terminal::Control::TermControl& control);
|
||||
TerminalTab(std::shared_ptr<Pane> rootPane);
|
||||
|
||||
// Called after construction to perform the necessary setup, which relies on weak_ptr
|
||||
void Initialize(const winrt::Microsoft::Terminal::Control::TermControl& control);
|
||||
void Initialize();
|
||||
|
||||
winrt::Microsoft::Terminal::Control::TermControl GetActiveTerminalControl() const;
|
||||
std::optional<GUID> GetFocusedProfile() const noexcept;
|
||||
winrt::Microsoft::Terminal::Settings::Model::Profile GetFocusedProfile() const noexcept;
|
||||
|
||||
void Focus(winrt::Windows::UI::Xaml::FocusState focusState) override;
|
||||
|
||||
winrt::fire_and_forget Scroll(const int delta);
|
||||
|
||||
std::shared_ptr<Pane> DetachRoot();
|
||||
std::shared_ptr<Pane> DetachPane();
|
||||
void AttachPane(std::shared_ptr<Pane> pane);
|
||||
|
||||
void SplitPane(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType,
|
||||
const float splitSize,
|
||||
const GUID& profile,
|
||||
const winrt::Microsoft::Terminal::Settings::Model::Profile& profile,
|
||||
winrt::Microsoft::Terminal::Control::TermControl& control);
|
||||
|
||||
void ToggleSplitOrientation();
|
||||
winrt::fire_and_forget UpdateIcon(const winrt::hstring iconPath);
|
||||
winrt::fire_and_forget HideIcon(const bool hide);
|
||||
|
||||
|
@ -52,10 +58,11 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
void ResizeContent(const winrt::Windows::Foundation::Size& newSize);
|
||||
void ResizePane(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction);
|
||||
void NavigateFocus(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
bool NavigateFocus(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
bool SwapPane(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
bool FocusPane(const uint32_t id);
|
||||
|
||||
void UpdateSettings(const Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, const GUID& profile);
|
||||
void UpdateSettings();
|
||||
winrt::fire_and_forget UpdateTitle();
|
||||
|
||||
void Shutdown() override;
|
||||
|
@ -81,6 +88,9 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
void TogglePaneReadOnly();
|
||||
std::shared_ptr<Pane> GetActivePane() const;
|
||||
winrt::TerminalApp::TaskbarState GetCombinedTaskbarState() const;
|
||||
|
||||
std::shared_ptr<Pane> GetRootPane() const { return _rootPane; }
|
||||
|
||||
winrt::TerminalApp::TerminalTabStatus TabStatus()
|
||||
{
|
||||
|
@ -92,12 +102,14 @@ namespace winrt::TerminalApp::implementation
|
|||
DECLARE_EVENT(ColorCleared, _colorCleared, winrt::delegate<>);
|
||||
DECLARE_EVENT(TabRaiseVisualBell, _TabRaiseVisualBellHandlers, winrt::delegate<>);
|
||||
DECLARE_EVENT(DuplicateRequested, _DuplicateRequestedHandlers, winrt::delegate<>);
|
||||
DECLARE_EVENT(SplitTabRequested, _SplitTabRequestedHandlers, winrt::delegate<>);
|
||||
TYPED_EVENT(TaskbarProgressChanged, IInspectable, IInspectable);
|
||||
|
||||
private:
|
||||
std::shared_ptr<Pane> _rootPane{ nullptr };
|
||||
std::shared_ptr<Pane> _activePane{ nullptr };
|
||||
std::shared_ptr<Pane> _zoomedPane{ nullptr };
|
||||
|
||||
winrt::hstring _lastIconPath{};
|
||||
winrt::TerminalApp::ColorPickupFlyout _tabColorPickup{};
|
||||
std::optional<winrt::Windows::UI::Color> _themeTabColor{};
|
||||
|
@ -105,6 +117,19 @@ namespace winrt::TerminalApp::implementation
|
|||
winrt::TerminalApp::TabHeaderControl _headerControl{};
|
||||
winrt::TerminalApp::TerminalTabStatus _tabStatus{};
|
||||
|
||||
struct ControlEventTokens
|
||||
{
|
||||
winrt::event_token titleToken;
|
||||
winrt::event_token fontToken;
|
||||
winrt::event_token colorToken;
|
||||
winrt::event_token taskbarToken;
|
||||
winrt::event_token readOnlyToken;
|
||||
winrt::event_token focusToken;
|
||||
};
|
||||
std::unordered_map<uint32_t, ControlEventTokens> _controlEvents;
|
||||
|
||||
winrt::event_token _rootClosedToken{};
|
||||
|
||||
std::vector<uint32_t> _mruPanes;
|
||||
uint32_t _nextPaneId{ 0 };
|
||||
|
||||
|
@ -117,6 +142,8 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
winrt::TerminalApp::ShortcutActionDispatch _dispatch;
|
||||
|
||||
void _Setup();
|
||||
|
||||
std::optional<Windows::UI::Xaml::DispatcherTimer> _bellIndicatorTimer;
|
||||
void _BellIndicatorTimerTick(Windows::Foundation::IInspectable const& sender, Windows::Foundation::IInspectable const& e);
|
||||
|
||||
|
@ -129,9 +156,8 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
void _RefreshVisualState();
|
||||
|
||||
void _BindEventHandlers(const winrt::Microsoft::Terminal::Control::TermControl& control) noexcept;
|
||||
|
||||
void _AttachEventHandlersToControl(const winrt::Microsoft::Terminal::Control::TermControl& control);
|
||||
void _DetachEventHandlersFromControl(const uint32_t paneId, const winrt::Microsoft::Terminal::Control::TermControl& control);
|
||||
void _AttachEventHandlersToControl(const uint32_t paneId, const winrt::Microsoft::Terminal::Control::TermControl& control);
|
||||
void _AttachEventHandlersToPane(std::shared_ptr<Pane> pane);
|
||||
|
||||
void _UpdateActivePane(std::shared_ptr<Pane> pane);
|
||||
|
|
|
@ -89,13 +89,13 @@
|
|||
</ItemGroup>
|
||||
|
||||
|
||||
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Import Project="$(OpenConsoleDir)packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" />
|
||||
</Target>
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.1.3" targetFramework="native" />
|
||||
<package id="Microsoft.UI.Xaml" version="2.5.0-prerelease.201202003" targetFramework="native" />
|
||||
<package id="Microsoft.UI.Xaml" version="2.6.2-prerelease.210818003" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
|
||||
</packages>
|
||||
|
|
|
@ -66,6 +66,7 @@ TRACELOGGING_DECLARE_PROVIDER(g_hTerminalAppProvider);
|
|||
#include <telemetry/ProjectTelemetry.h>
|
||||
#include <TraceLoggingActivity.h>
|
||||
|
||||
#include <msctf.h>
|
||||
#include <shellapi.h>
|
||||
#include <shobjidl_core.h>
|
||||
|
||||
|
|
|
@ -96,7 +96,11 @@ int wmain(int /*argc*/, wchar_t** /*argv*/)
|
|||
|
||||
const auto size = GetConsoleScreenSize(conOut);
|
||||
|
||||
AzureConnection azureConn{ gsl::narrow_cast<uint32_t>(size.Y), gsl::narrow_cast<uint32_t>(size.X) };
|
||||
AzureConnection azureConn{};
|
||||
winrt::Windows::Foundation::Collections::ValueSet vs{};
|
||||
vs.Insert(L"initialRows", winrt::Windows::Foundation::PropertyValue::CreateUInt32(gsl::narrow_cast<uint32_t>(size.Y)));
|
||||
vs.Insert(L"initialCols", winrt::Windows::Foundation::PropertyValue::CreateUInt32(gsl::narrow_cast<uint32_t>(size.X)));
|
||||
azureConn.Initialize(vs);
|
||||
|
||||
const auto state = RunConnectionToCompletion(azureConn, conOut, conIn);
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ Abstract:
|
|||
#include <wil/cppwinrt.h>
|
||||
|
||||
#include <winrt/Windows.system.h>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
|
||||
#include <wil/resource.h>
|
||||
|
|
|
@ -71,11 +71,13 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
|||
return (AzureClientID != L"0");
|
||||
}
|
||||
|
||||
AzureConnection::AzureConnection(const uint32_t initialRows, const uint32_t initialCols) :
|
||||
_initialRows{ initialRows },
|
||||
_initialCols{ initialCols },
|
||||
_expiry{}
|
||||
void AzureConnection::Initialize(const Windows::Foundation::Collections::ValueSet& settings)
|
||||
{
|
||||
if (settings)
|
||||
{
|
||||
_initialRows = winrt::unbox_value_or<uint32_t>(settings.TryLookup(L"initialRows").try_as<Windows::Foundation::IPropertyValue>(), _initialRows);
|
||||
_initialCols = winrt::unbox_value_or<uint32_t>(settings.TryLookup(L"initialCols").try_as<Windows::Foundation::IPropertyValue>(), _initialCols);
|
||||
}
|
||||
}
|
||||
|
||||
// Method description:
|
||||
|
|
|
@ -21,7 +21,9 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
|||
{
|
||||
static winrt::guid ConnectionType() noexcept;
|
||||
static bool IsAzureConnectionAvailable() noexcept;
|
||||
AzureConnection(const uint32_t rows, const uint32_t cols);
|
||||
|
||||
AzureConnection() = default;
|
||||
void Initialize(const Windows::Foundation::Collections::ValueSet& settings);
|
||||
|
||||
void Start();
|
||||
void WriteInput(hstring const& data);
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace Microsoft.Terminal.TerminalConnection
|
|||
static Guid ConnectionType { get; };
|
||||
static Boolean IsAzureConnectionAvailable();
|
||||
|
||||
AzureConnection(UInt32 rows, UInt32 columns);
|
||||
AzureConnection();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ using namespace Microsoft::WRL;
|
|||
static NewHandoffFunction _pfnHandoff = nullptr;
|
||||
// The registration ID of the class object for clean up later
|
||||
static DWORD g_cTerminalHandoffRegistration = 0;
|
||||
// Mutex so we only do start/stop/establish one at a time.
|
||||
static std::shared_mutex _mtx;
|
||||
|
||||
// Routine Description:
|
||||
// - Starts listening for TerminalHandoff requests by registering
|
||||
|
@ -19,9 +21,11 @@ static DWORD g_cTerminalHandoffRegistration = 0;
|
|||
// - 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) noexcept
|
||||
HRESULT CTerminalHandoff::s_StartListening(NewHandoffFunction pfnHandoff)
|
||||
try
|
||||
{
|
||||
std::unique_lock lock{ _mtx };
|
||||
|
||||
RETURN_HR_IF(E_NOT_VALID_STATE, _pfnHandoff != nullptr);
|
||||
|
||||
const auto classFactory = Make<SimpleClassFactory<CTerminalHandoff>>();
|
||||
|
@ -31,7 +35,7 @@ try
|
|||
ComPtr<IUnknown> unk;
|
||||
RETURN_IF_FAILED(classFactory.As(&unk));
|
||||
|
||||
RETURN_IF_FAILED(CoRegisterClassObject(__uuidof(CTerminalHandoff), unk.Get(), CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &g_cTerminalHandoffRegistration));
|
||||
RETURN_IF_FAILED(CoRegisterClassObject(__uuidof(CTerminalHandoff), unk.Get(), CLSCTX_LOCAL_SERVER, REGCLS_SINGLEUSE, &g_cTerminalHandoffRegistration));
|
||||
|
||||
_pfnHandoff = pfnHandoff;
|
||||
|
||||
|
@ -46,8 +50,10 @@ CATCH_RETURN()
|
|||
// - <none>
|
||||
// Return Value:
|
||||
// - S_OK, E_NOT_VALID_STATE (stop called when not started), or relevant COM class revoke error
|
||||
HRESULT CTerminalHandoff::s_StopListening() noexcept
|
||||
HRESULT CTerminalHandoff::s_StopListening()
|
||||
{
|
||||
std::unique_lock lock{ _mtx };
|
||||
|
||||
RETURN_HR_IF_NULL(E_NOT_VALID_STATE, _pfnHandoff);
|
||||
|
||||
_pfnHandoff = nullptr;
|
||||
|
@ -91,10 +97,19 @@ static HRESULT _duplicateHandle(const HANDLE in, HANDLE& out) noexcept
|
|||
// - E_NOT_VALID_STATE if a event handler is not registered before calling. `::DuplicateHandle`
|
||||
// error codes if we cannot manage to make our own copy of handles to retain. Or S_OK/error
|
||||
// from the registered handler event function.
|
||||
HRESULT CTerminalHandoff::EstablishPtyHandoff(HANDLE in, HANDLE out, HANDLE signal, HANDLE ref, HANDLE server, HANDLE client) noexcept
|
||||
HRESULT CTerminalHandoff::EstablishPtyHandoff(HANDLE in, HANDLE out, HANDLE signal, HANDLE ref, HANDLE server, HANDLE client)
|
||||
{
|
||||
// Stash a local copy of _pfnHandoff before we stop listening.
|
||||
auto localPfnHandoff = _pfnHandoff;
|
||||
|
||||
// Because we are REGCLS_SINGLEUSE... we need to `CoRevokeClassObject` after we handle this ONE call.
|
||||
// COM does not automatically clean that up for us. We must do it.
|
||||
s_StopListening();
|
||||
|
||||
std::unique_lock lock{ _mtx };
|
||||
|
||||
// Report an error if no one registered a handoff function before calling this.
|
||||
RETURN_HR_IF_NULL(E_NOT_VALID_STATE, _pfnHandoff);
|
||||
RETURN_HR_IF_NULL(E_NOT_VALID_STATE, localPfnHandoff);
|
||||
|
||||
// Duplicate the handles from what we received.
|
||||
// The contract with COM specifies that any HANDLEs we receive from the caller belong
|
||||
|
@ -108,5 +123,5 @@ HRESULT CTerminalHandoff::EstablishPtyHandoff(HANDLE in, HANDLE out, HANDLE sign
|
|||
RETURN_IF_FAILED(_duplicateHandle(client, client));
|
||||
|
||||
// Call registered handler from when we started listening.
|
||||
return _pfnHandoff(in, out, signal, ref, server, client);
|
||||
return localPfnHandoff(in, out, signal, ref, server, client);
|
||||
}
|
||||
|
|
|
@ -37,12 +37,12 @@ struct __declspec(uuid(__CLSID_CTerminalHandoff))
|
|||
HANDLE signal,
|
||||
HANDLE ref,
|
||||
HANDLE server,
|
||||
HANDLE client) noexcept override;
|
||||
HANDLE client) override;
|
||||
|
||||
#pragma endregion
|
||||
|
||||
static HRESULT s_StartListening(NewHandoffFunction pfnHandoff) noexcept;
|
||||
static HRESULT s_StopListening() noexcept;
|
||||
static HRESULT s_StartListening(NewHandoffFunction pfnHandoff);
|
||||
static HRESULT s_StopListening();
|
||||
};
|
||||
|
||||
// Disable warnings from the CoCreatableClass macro as the value it provides for
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
#include "pch.h"
|
||||
#include "ConnectionInformation.h"
|
||||
#include "ConnectionInformation.g.cpp"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||
{
|
||||
ConnectionInformation::ConnectionInformation(hstring const& className,
|
||||
const Windows::Foundation::Collections::ValueSet& settings) :
|
||||
_ClassName{ className },
|
||||
_Settings{ settings }
|
||||
{
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Create an instance of the connection specified in the
|
||||
// ConnectionInformation, and Initialize it.
|
||||
// - This static method allows the content process to create a connection
|
||||
// from information that lives in the window process.
|
||||
// Arguments:
|
||||
// - info: A ConnectionInformation object that possibly lives out-of-proc,
|
||||
// containing the name of the WinRT class we should activate for this
|
||||
// connection, and a bag of setting to use to initialize that object.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
TerminalConnection::ITerminalConnection ConnectionInformation::CreateConnection(TerminalConnection::ConnectionInformation info)
|
||||
try
|
||||
{
|
||||
Windows::Foundation::IInspectable inspectable{};
|
||||
|
||||
const auto name = static_cast<HSTRING>(winrt::get_abi(info.ClassName()));
|
||||
const auto pointer = winrt::put_abi(inspectable);
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 26490)
|
||||
// C++/WinRT just loves it's void**, nothing we can do here _except_ reinterpret_cast
|
||||
::IInspectable** raw = reinterpret_cast<::IInspectable**>(pointer);
|
||||
#pragma warning(pop)
|
||||
|
||||
// RoActivateInstance() will try to create an instance of the object,
|
||||
// who's fully qualified name is the string in Name().
|
||||
//
|
||||
// The class has to be activatable. For the Terminal, this is easy
|
||||
// enough - we're not hosting anything that's not already in our
|
||||
// manifest, or living as a .dll & .winmd SxS.
|
||||
//
|
||||
// When we get to extensions (GH#4000), we may want to revisit.
|
||||
if (LOG_IF_FAILED(RoActivateInstance(name, raw)))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Now that thing we made, make sure it's actually a ITerminalConnection
|
||||
if (const auto connection{ inspectable.try_as<TerminalConnection::ITerminalConnection>() })
|
||||
{
|
||||
// Initialize it, and return it.
|
||||
connection.Initialize(info.Settings());
|
||||
return connection;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Class Name:
|
||||
- ConnectionInformation.h
|
||||
|
||||
Abstract:
|
||||
- This is a helper object for storing both the name of a type of connection, and
|
||||
a bag of settings to use to initialize that connection.
|
||||
- This helper is used primarily in cross-proc scenarios, to allow the window
|
||||
process to tell the content process the name of the connection type it wants
|
||||
created, and how to set that connection up. This is done so the connection can
|
||||
live entirely in the content process, without having to go through the window
|
||||
process at all.
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
#include "../inc/cppwinrt_utils.h"
|
||||
#include "ConnectionInformation.g.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||
{
|
||||
struct ConnectionInformation : ConnectionInformationT<ConnectionInformation>
|
||||
{
|
||||
ConnectionInformation(hstring const& className,
|
||||
const Windows::Foundation::Collections::ValueSet& settings);
|
||||
|
||||
static TerminalConnection::ITerminalConnection CreateConnection(TerminalConnection::ConnectionInformation info);
|
||||
|
||||
winrt::hstring ClassName() const { return _ClassName; }
|
||||
void ClassName(const winrt::hstring& value) { _ClassName = value; }
|
||||
|
||||
WINRT_PROPERTY(Windows::Foundation::Collections::ValueSet, Settings);
|
||||
|
||||
private:
|
||||
winrt::hstring _ClassName{};
|
||||
};
|
||||
}
|
||||
namespace winrt::Microsoft::Terminal::TerminalConnection::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(ConnectionInformation);
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import "ITerminalConnection.idl";
|
||||
|
||||
namespace Microsoft.Terminal.TerminalConnection
|
||||
{
|
||||
[default_interface] runtimeclass ConnectionInformation
|
||||
{
|
||||
ConnectionInformation(String className, Windows.Foundation.Collections.ValueSet settings);
|
||||
String ClassName { get; };
|
||||
Windows.Foundation.Collections.ValueSet Settings { get; };
|
||||
|
||||
static ITerminalConnection CreateConnection(ConnectionInformation info);
|
||||
}
|
||||
|
||||
}
|
|
@ -57,6 +57,72 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Promotes a starting directory provided to a WSL invocation to a commandline argument.
|
||||
// This is necessary because WSL has some modicum of support for linux-side directories (!) which
|
||||
// CreateProcess never will.
|
||||
static std::tuple<std::wstring, std::wstring> _tryMangleStartingDirectoryForWSL(std::wstring_view commandLine, std::wstring_view startingDirectory)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (startingDirectory.size() > 0 && commandLine.size() >= 3)
|
||||
{ // "wsl" is three characters; this is a safe bet. no point in doing it if there's no starting directory though!
|
||||
// Find the first space, quote or the end of the string -- we'll look for wsl before that.
|
||||
const auto terminator{ commandLine.find_first_of(LR"(" )", 1) }; // look past the first character in case it starts with "
|
||||
const auto start{ til::at(commandLine, 0) == L'"' ? 1 : 0 };
|
||||
const std::filesystem::path executablePath{ commandLine.substr(start, terminator - start) };
|
||||
const auto executableFilename{ executablePath.filename().wstring() };
|
||||
if (executableFilename == L"wsl" || executableFilename == L"wsl.exe")
|
||||
{
|
||||
// We've got a WSL -- let's just make sure it's the right one.
|
||||
if (executablePath.has_parent_path())
|
||||
{
|
||||
std::wstring systemDirectory{};
|
||||
if (FAILED(wil::GetSystemDirectoryW(systemDirectory)))
|
||||
{
|
||||
break; // just bail out.
|
||||
}
|
||||
if (executablePath.parent_path().wstring() != systemDirectory)
|
||||
{
|
||||
break; // it wasn't in system32!
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// assume that unqualified WSL is the one in system32 (minor danger)
|
||||
}
|
||||
|
||||
const auto arguments{ terminator == std::wstring_view::npos ? std::wstring_view{} : commandLine.substr(terminator + 1) };
|
||||
if (arguments.find(L"--cd") != std::wstring_view::npos)
|
||||
{
|
||||
break; // they've already got a --cd!
|
||||
}
|
||||
|
||||
const auto tilde{ arguments.find_first_of(L'~') };
|
||||
if (tilde != std::wstring_view::npos)
|
||||
{
|
||||
if (tilde + 1 == arguments.size() || til::at(arguments, tilde + 1) == L' ')
|
||||
{
|
||||
// We want to suppress --cd if they have added a bare ~ to their commandline (they conflict).
|
||||
break;
|
||||
}
|
||||
// Tilde followed by non-space should be okay (like, wsl -d Debian ~/blah.sh)
|
||||
}
|
||||
|
||||
return {
|
||||
fmt::format(LR"("{}" --cd "{}" {})", executablePath.wstring(), startingDirectory, arguments),
|
||||
std::wstring{}
|
||||
};
|
||||
}
|
||||
}
|
||||
} while (false);
|
||||
|
||||
return {
|
||||
std::wstring{ commandLine },
|
||||
std::wstring{ startingDirectory }
|
||||
};
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - launches the client application attached to the new pseudoconsole
|
||||
HRESULT ConptyConnection::_LaunchAttachedClient() noexcept
|
||||
|
@ -116,17 +182,23 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
|||
// add additional WT env vars like WT_SETTINGS, WT_DEFAULTS and WT_PROFILE_ID
|
||||
for (auto item : _environment)
|
||||
{
|
||||
auto key = item.Key();
|
||||
auto value = item.Value();
|
||||
|
||||
// avoid clobbering WSLENV
|
||||
if (std::wstring_view{ key } == L"WSLENV")
|
||||
try
|
||||
{
|
||||
auto current = environment[L"WSLENV"];
|
||||
value = current + L":" + value;
|
||||
}
|
||||
auto key = item.Key();
|
||||
// This will throw if the value isn't a string. If that
|
||||
// happens, then just skip this entry.
|
||||
auto value = winrt::unbox_value<hstring>(item.Value());
|
||||
|
||||
environment.insert_or_assign(key.c_str(), value.c_str());
|
||||
// avoid clobbering WSLENV
|
||||
if (std::wstring_view{ key } == L"WSLENV")
|
||||
{
|
||||
auto current = environment[L"WSLENV"];
|
||||
value = current + L":" + value;
|
||||
}
|
||||
|
||||
environment.insert_or_assign(key.c_str(), value.c_str());
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -157,11 +229,12 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
|||
siEx.StartupInfo.lpTitle = mutableTitle.data();
|
||||
}
|
||||
|
||||
const wchar_t* const startingDirectory = _startingDirectory.size() > 0 ? _startingDirectory.c_str() : nullptr;
|
||||
auto [newCommandLine, newStartingDirectory] = _tryMangleStartingDirectoryForWSL(cmdline, _startingDirectory);
|
||||
const wchar_t* const startingDirectory = newStartingDirectory.size() > 0 ? newStartingDirectory.c_str() : nullptr;
|
||||
|
||||
RETURN_IF_WIN32_BOOL_FALSE(CreateProcessW(
|
||||
nullptr,
|
||||
cmdline.data(),
|
||||
newCommandLine.data(),
|
||||
nullptr, // lpProcessAttributes
|
||||
nullptr, // lpThreadAttributes
|
||||
false, // bInheritHandles
|
||||
|
@ -219,24 +292,54 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
|||
_piClient.hProcess = hClientProcess;
|
||||
}
|
||||
|
||||
ConptyConnection::ConptyConnection(const hstring& commandline,
|
||||
const hstring& startingDirectory,
|
||||
const hstring& startingTitle,
|
||||
const Windows::Foundation::Collections::IMapView<hstring, hstring>& environment,
|
||||
const uint32_t initialRows,
|
||||
const uint32_t initialCols,
|
||||
const guid& initialGuid) :
|
||||
_initialRows{ initialRows },
|
||||
_initialCols{ initialCols },
|
||||
_commandline{ commandline },
|
||||
_startingDirectory{ startingDirectory },
|
||||
_startingTitle{ startingTitle },
|
||||
_environment{ environment },
|
||||
_guid{ initialGuid },
|
||||
_u8State{},
|
||||
_u16Str{},
|
||||
_buffer{}
|
||||
// Function Description:
|
||||
// - Helper function for constructing a ValueSet that we can use to get our settings from.
|
||||
Windows::Foundation::Collections::ValueSet ConptyConnection::CreateSettings(const winrt::hstring& cmdline,
|
||||
const winrt::hstring& startingDirectory,
|
||||
const winrt::hstring& startingTitle,
|
||||
Windows::Foundation::Collections::IMapView<hstring, hstring> const& environment,
|
||||
uint32_t rows,
|
||||
uint32_t columns,
|
||||
winrt::guid const& guid)
|
||||
{
|
||||
Windows::Foundation::Collections::ValueSet vs{};
|
||||
|
||||
vs.Insert(L"commandline", Windows::Foundation::PropertyValue::CreateString(cmdline));
|
||||
vs.Insert(L"startingDirectory", Windows::Foundation::PropertyValue::CreateString(startingDirectory));
|
||||
vs.Insert(L"startingTitle", Windows::Foundation::PropertyValue::CreateString(startingTitle));
|
||||
vs.Insert(L"initialRows", Windows::Foundation::PropertyValue::CreateUInt32(rows));
|
||||
vs.Insert(L"initialCols", Windows::Foundation::PropertyValue::CreateUInt32(columns));
|
||||
vs.Insert(L"guid", Windows::Foundation::PropertyValue::CreateGuid(guid));
|
||||
|
||||
if (environment)
|
||||
{
|
||||
Windows::Foundation::Collections::ValueSet env{};
|
||||
for (const auto& [k, v] : environment)
|
||||
{
|
||||
env.Insert(k, Windows::Foundation::PropertyValue::CreateString(v));
|
||||
}
|
||||
vs.Insert(L"environment", env);
|
||||
}
|
||||
return vs;
|
||||
}
|
||||
|
||||
void ConptyConnection::Initialize(const Windows::Foundation::Collections::ValueSet& settings)
|
||||
{
|
||||
if (settings)
|
||||
{
|
||||
// For the record, the following won't crash:
|
||||
// auto bad = unbox_value_or<hstring>(settings.TryLookup(L"foo").try_as<IPropertyValue>(), nullptr);
|
||||
// It'll just return null
|
||||
|
||||
_commandline = winrt::unbox_value_or<winrt::hstring>(settings.TryLookup(L"commandline").try_as<Windows::Foundation::IPropertyValue>(), _commandline);
|
||||
_startingDirectory = winrt::unbox_value_or<winrt::hstring>(settings.TryLookup(L"startingDirectory").try_as<Windows::Foundation::IPropertyValue>(), _startingDirectory);
|
||||
_startingTitle = winrt::unbox_value_or<winrt::hstring>(settings.TryLookup(L"startingTitle").try_as<Windows::Foundation::IPropertyValue>(), _startingTitle);
|
||||
_initialRows = winrt::unbox_value_or<uint32_t>(settings.TryLookup(L"initialRows").try_as<Windows::Foundation::IPropertyValue>(), _initialRows);
|
||||
_initialCols = winrt::unbox_value_or<uint32_t>(settings.TryLookup(L"initialCols").try_as<Windows::Foundation::IPropertyValue>(), _initialCols);
|
||||
_guid = winrt::unbox_value_or<winrt::guid>(settings.TryLookup(L"guid").try_as<Windows::Foundation::IPropertyValue>(), _guid);
|
||||
_environment = settings.TryLookup(L"environment").try_as<Windows::Foundation::Collections::ValueSet>();
|
||||
}
|
||||
|
||||
if (_guid == guid{})
|
||||
{
|
||||
_guid = Utils::CreateGuid();
|
||||
|
@ -253,12 +356,21 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
|||
{
|
||||
_transitionToState(ConnectionState::Connecting);
|
||||
|
||||
const COORD dimensions{ gsl::narrow_cast<SHORT>(_initialCols), gsl::narrow_cast<SHORT>(_initialRows) };
|
||||
|
||||
// If we do not have pipes already, then this is a fresh connection... not an inbound one that is a received
|
||||
// handoff from an already-started PTY process.
|
||||
if (!_inPipe)
|
||||
{
|
||||
const COORD dimensions{ gsl::narrow_cast<SHORT>(_initialCols), gsl::narrow_cast<SHORT>(_initialRows) };
|
||||
THROW_IF_FAILED(_CreatePseudoConsoleAndPipes(dimensions, PSEUDOCONSOLE_RESIZE_QUIRK | PSEUDOCONSOLE_WIN32_INPUT_MODE, &_inPipe, &_outPipe, &_hPC));
|
||||
THROW_IF_FAILED(_LaunchAttachedClient());
|
||||
}
|
||||
// But if it was an inbound handoff... attempt to synchronize the size of it with what our connection
|
||||
// window is expecting it to be on the first layout.
|
||||
else
|
||||
{
|
||||
THROW_IF_FAILED(ConptyResizePseudoConsole(_hPC.get(), dimensions));
|
||||
}
|
||||
|
||||
_startTime = std::chrono::high_resolution_clock::now();
|
||||
|
||||
|
@ -387,11 +499,14 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
|||
|
||||
void ConptyConnection::Resize(uint32_t rows, uint32_t columns)
|
||||
{
|
||||
if (!_hPC)
|
||||
// If we haven't started connecting at all, it's still fair to update
|
||||
// the initial rows and columns before we set things up.
|
||||
if (!_isStateAtOrBeyond(ConnectionState::Connecting))
|
||||
{
|
||||
_initialRows = rows;
|
||||
_initialCols = columns;
|
||||
}
|
||||
// Otherwise, we can really only dispatch a resize if we're already connected.
|
||||
else if (_isConnected())
|
||||
{
|
||||
THROW_IF_FAILED(ConptyResizePseudoConsole(_hPC.get(), { Utils::ClampToShortMax(columns, 1), Utils::ClampToShortMax(rows, 1) }));
|
||||
|
|
|
@ -26,14 +26,9 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
|||
const HANDLE hServerProcess,
|
||||
const HANDLE hClientProcess);
|
||||
|
||||
ConptyConnection(
|
||||
const hstring& cmdline,
|
||||
const hstring& startingDirectory,
|
||||
const hstring& startingTitle,
|
||||
const Windows::Foundation::Collections::IMapView<hstring, hstring>& environment,
|
||||
const uint32_t rows,
|
||||
const uint32_t cols,
|
||||
const guid& guid);
|
||||
ConptyConnection() noexcept = default;
|
||||
void Initialize(const Windows::Foundation::Collections::ValueSet& settings);
|
||||
|
||||
static winrt::fire_and_forget final_release(std::unique_ptr<ConptyConnection> connection);
|
||||
|
||||
void Start();
|
||||
|
@ -49,6 +44,14 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
|||
static winrt::event_token NewConnection(NewConnectionHandler const& handler);
|
||||
static void NewConnection(winrt::event_token const& token);
|
||||
|
||||
static Windows::Foundation::Collections::ValueSet CreateSettings(const winrt::hstring& cmdline,
|
||||
const winrt::hstring& startingDirectory,
|
||||
const winrt::hstring& startingTitle,
|
||||
Windows::Foundation::Collections::IMapView<hstring, hstring> const& environment,
|
||||
uint32_t rows,
|
||||
uint32_t columns,
|
||||
winrt::guid const& guid);
|
||||
|
||||
WINRT_CALLBACK(TerminalOutput, TerminalOutputHandler);
|
||||
|
||||
private:
|
||||
|
@ -60,10 +63,10 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
|||
|
||||
uint32_t _initialRows{};
|
||||
uint32_t _initialCols{};
|
||||
hstring _commandline;
|
||||
hstring _startingDirectory;
|
||||
hstring _startingTitle;
|
||||
Windows::Foundation::Collections::IMapView<hstring, hstring> _environment;
|
||||
hstring _commandline{};
|
||||
hstring _startingDirectory{};
|
||||
hstring _startingTitle{};
|
||||
Windows::Foundation::Collections::ValueSet _environment{ nullptr };
|
||||
guid _guid{}; // A unique session identifier for connected client
|
||||
hstring _clientName{}; // The name of the process hosted by this ConPTY connection (as of launch).
|
||||
|
||||
|
@ -77,9 +80,9 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
|||
wil::unique_static_pseudoconsole_handle _hPC;
|
||||
wil::unique_threadpool_wait _clientExitWait;
|
||||
|
||||
til::u8state _u8State;
|
||||
std::wstring _u16Str;
|
||||
std::array<char, 4096> _buffer;
|
||||
til::u8state _u8State{};
|
||||
std::wstring _u16Str{};
|
||||
std::array<char, 4096> _buffer{};
|
||||
|
||||
DWORD _OutputThread();
|
||||
};
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue