mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-30 17:07:23 +01:00
Compare commits
67 Commits
leilzh/ima
...
shawn/quic
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa262cdf20 | ||
|
|
bb3dee489b | ||
|
|
01aa491962 | ||
|
|
ee0d931f39 | ||
|
|
eceb55ca9f | ||
|
|
37bd24db36 | ||
|
|
557a07589d | ||
|
|
2603efc8a9 | ||
|
|
dd138fb94b | ||
|
|
7cd201d355 | ||
|
|
9aab0f3893 | ||
|
|
91b7a99e76 | ||
|
|
d38edf798d | ||
|
|
17668047bf | ||
|
|
e941ea4ebf | ||
|
|
7b0b284d40 | ||
|
|
9aca6d136f | ||
|
|
4b2ee60b42 | ||
|
|
e37a328624 | ||
|
|
b1cf7fa9da | ||
|
|
346c99543a | ||
|
|
1211e2c92f | ||
|
|
9f99727bf5 | ||
|
|
fee146b084 | ||
|
|
91cceb70d5 | ||
|
|
b73908c413 | ||
|
|
062589b9f7 | ||
|
|
ff21b77b7b | ||
|
|
d6ae07c040 | ||
|
|
e038a09357 | ||
|
|
f6e6fe676a | ||
|
|
debf4322df | ||
|
|
4a823320c4 | ||
|
|
e89d6d8f5a | ||
|
|
2ca7531ae7 | ||
|
|
6ba33b0d91 | ||
|
|
273eebda25 | ||
|
|
fdd34bca09 | ||
|
|
dd9643dd38 | ||
|
|
eb69549320 | ||
|
|
8f6bd72679 | ||
|
|
4865b44de1 | ||
|
|
3381e82fc1 | ||
|
|
a40be6f9be | ||
|
|
90e4f1ca41 | ||
|
|
1fcb8e7515 | ||
|
|
3ed9641072 | ||
|
|
0428cf45af | ||
|
|
5fc0ae7e42 | ||
|
|
8cdd8574d6 | ||
|
|
f564cf9189 | ||
|
|
e826cfb491 | ||
|
|
45e94f2ce0 | ||
|
|
b3b99d6d11 | ||
|
|
bb180a166a | ||
|
|
7e62c76c18 | ||
|
|
68dd7a46f0 | ||
|
|
a67326d86d | ||
|
|
16733718c3 | ||
|
|
5bf797d9f6 | ||
|
|
8de7110918 | ||
|
|
ed254b60cc | ||
|
|
ea0a13be3a | ||
|
|
8e6f40ffe9 | ||
|
|
891dd41cc2 | ||
|
|
d7e727fa1a | ||
|
|
709ceed137 |
14
.github/actions/spell-check/expect.txt
vendored
14
.github/actions/spell-check/expect.txt
vendored
@@ -224,7 +224,6 @@ clientside
|
||||
CLIPBOARDUPDATE
|
||||
CLIPCHILDREN
|
||||
CLIPSIBLINGS
|
||||
CLITo
|
||||
closesocket
|
||||
clp
|
||||
CLSCTX
|
||||
@@ -325,7 +324,6 @@ CUSTOMFORMATPLACEHOLDER
|
||||
CVal
|
||||
cvd
|
||||
CVirtual
|
||||
CVS
|
||||
CWMO
|
||||
CXSCREEN
|
||||
CXSMICON
|
||||
@@ -365,6 +363,7 @@ DEFAULTICON
|
||||
defaultlib
|
||||
DEFAULTONLY
|
||||
DEFAULTTONEAREST
|
||||
Defaulttonearest
|
||||
DEFAULTTONULL
|
||||
DEFAULTTOPRIMARY
|
||||
DEFERERASE
|
||||
@@ -858,6 +857,7 @@ lastcodeanalysissucceeded
|
||||
LASTEXITCODE
|
||||
LAYOUTRTL
|
||||
lbl
|
||||
Lbuttondown
|
||||
LCh
|
||||
lcid
|
||||
LCIDTo
|
||||
@@ -978,6 +978,7 @@ maxversiontested
|
||||
mber
|
||||
MBM
|
||||
MBR
|
||||
Mbuttondown
|
||||
MDICHILD
|
||||
MDL
|
||||
mdtext
|
||||
@@ -1078,7 +1079,6 @@ MVVMTK
|
||||
MWBEx
|
||||
MYICON
|
||||
NAMECHANGE
|
||||
Notavailable
|
||||
namespaceanddescendants
|
||||
nao
|
||||
NCACTIVATE
|
||||
@@ -1431,6 +1431,7 @@ RAWINPUTHEADER
|
||||
RAWMODE
|
||||
RAWPATH
|
||||
rbhid
|
||||
Rbuttondown
|
||||
rclsid
|
||||
RCZOOMIT
|
||||
remotedesktop
|
||||
@@ -1730,6 +1731,7 @@ svgz
|
||||
SVSI
|
||||
SWFO
|
||||
SWP
|
||||
Swp
|
||||
SWPNOSIZE
|
||||
SWPNOZORDER
|
||||
SWRESTORE
|
||||
@@ -1749,6 +1751,7 @@ syskeydown
|
||||
SYSKEYUP
|
||||
SYSLIB
|
||||
SYSMENU
|
||||
Sysmenu
|
||||
systemai
|
||||
SYSTEMAPPS
|
||||
SYSTEMMODAL
|
||||
@@ -1805,7 +1808,6 @@ tlbimp
|
||||
tlc
|
||||
tmain
|
||||
TNP
|
||||
toolgood
|
||||
Toolhelp
|
||||
toolwindow
|
||||
TOPDOWNDIB
|
||||
@@ -1867,7 +1869,6 @@ Uniquifies
|
||||
unitconverter
|
||||
unittests
|
||||
UNLEN
|
||||
Uninitializes
|
||||
UNORM
|
||||
unremapped
|
||||
Unsubscribes
|
||||
@@ -1955,7 +1956,7 @@ vswhere
|
||||
Vtbl
|
||||
WANTNUKEWARNING
|
||||
WANTPALM
|
||||
wasdk
|
||||
WASDK
|
||||
wbem
|
||||
WBounds
|
||||
Wca
|
||||
@@ -2076,6 +2077,7 @@ Wwanpp
|
||||
xap
|
||||
XAxis
|
||||
XButton
|
||||
Xbuttondown
|
||||
xclip
|
||||
xcopy
|
||||
XDeployment
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Param(
|
||||
# Using the default value of 1.7 for winAppSdkVersionNumber and useExperimentalVersion as false
|
||||
[Parameter(Mandatory=$False,Position=1)]
|
||||
[string]$winAppSdkVersionNumber = "1.7",
|
||||
[string]$winAppSdkVersionNumber = "1.8",
|
||||
|
||||
# When the pipeline calls the PS1 file, the passed parameters are converted to string type
|
||||
[Parameter(Mandatory=$False,Position=2)]
|
||||
@@ -16,32 +16,7 @@ Param(
|
||||
[string]$sourceLink = "https://microsoft.pkgs.visualstudio.com/ProjectReunion/_packaging/Project.Reunion.nuget.internal/nuget/v3/index.json"
|
||||
)
|
||||
|
||||
function Update-NugetConfig {
|
||||
param (
|
||||
[string]$filePath = [System.IO.Path]::Combine($rootPath, "nuget.config")
|
||||
)
|
||||
|
||||
Write-Host "Updating nuget.config file"
|
||||
[xml]$xml = Get-Content -Path $filePath
|
||||
|
||||
# Add localpackages source into nuget.config
|
||||
$packageSourcesNode = $xml.configuration.packageSources
|
||||
$addNode = $xml.CreateElement("add")
|
||||
$addNode.SetAttribute("key", "localpackages")
|
||||
$addNode.SetAttribute("value", "localpackages")
|
||||
$packageSourcesNode.AppendChild($addNode) | Out-Null
|
||||
|
||||
# Remove <packageSourceMapping> tag and its content
|
||||
$packageSourceMappingNode = $xml.configuration.packageSourceMapping
|
||||
if ($packageSourceMappingNode) {
|
||||
$xml.configuration.RemoveChild($packageSourceMappingNode) | Out-Null
|
||||
}
|
||||
|
||||
# print nuget.config after modification
|
||||
$xml.OuterXml
|
||||
# Save the modified nuget.config file
|
||||
$xml.Save($filePath)
|
||||
}
|
||||
|
||||
function Read-FileWithEncoding {
|
||||
param (
|
||||
@@ -71,6 +46,132 @@ function Write-FileWithEncoding {
|
||||
$writer.Close()
|
||||
}
|
||||
|
||||
|
||||
function Add-NuGetSourceAndMapping {
|
||||
param (
|
||||
[xml]$Xml,
|
||||
[string]$Key,
|
||||
[string]$Value,
|
||||
[string[]]$Patterns
|
||||
)
|
||||
|
||||
# Ensure packageSources exists
|
||||
if (-not $Xml.configuration.packageSources) {
|
||||
$Xml.configuration.AppendChild($Xml.CreateElement("packageSources")) | Out-Null
|
||||
}
|
||||
$sources = $Xml.configuration.packageSources
|
||||
|
||||
# Add/Update Source
|
||||
$sourceNode = $sources.SelectSingleNode("add[@key='$Key']")
|
||||
if (-not $sourceNode) {
|
||||
$sourceNode = $Xml.CreateElement("add")
|
||||
$sourceNode.SetAttribute("key", $Key)
|
||||
$sources.AppendChild($sourceNode) | Out-Null
|
||||
}
|
||||
$sourceNode.SetAttribute("value", $Value)
|
||||
|
||||
# Ensure packageSourceMapping exists
|
||||
if (-not $Xml.configuration.packageSourceMapping) {
|
||||
$Xml.configuration.AppendChild($Xml.CreateElement("packageSourceMapping")) | Out-Null
|
||||
}
|
||||
$mapping = $Xml.configuration.packageSourceMapping
|
||||
|
||||
# Remove invalid packageSource nodes (missing key or empty key)
|
||||
$invalidNodes = $mapping.SelectNodes("packageSource[not(@key) or @key='']")
|
||||
if ($invalidNodes) {
|
||||
foreach ($node in $invalidNodes) {
|
||||
$mapping.RemoveChild($node) | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
# Add/Update Mapping Source
|
||||
$mappingSource = $mapping.SelectSingleNode("packageSource[@key='$Key']")
|
||||
if (-not $mappingSource) {
|
||||
$mappingSource = $Xml.CreateElement("packageSource")
|
||||
$mappingSource.SetAttribute("key", $Key)
|
||||
# Insert at top for priority
|
||||
if ($mapping.HasChildNodes) {
|
||||
$mapping.InsertBefore($mappingSource, $mapping.FirstChild) | Out-Null
|
||||
} else {
|
||||
$mapping.AppendChild($mappingSource) | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
# Double check and force attribute
|
||||
if (-not $mappingSource.HasAttribute("key")) {
|
||||
$mappingSource.SetAttribute("key", $Key)
|
||||
}
|
||||
|
||||
# Update Patterns
|
||||
# RemoveAll() removes all child nodes AND attributes, so we must re-set the key afterwards
|
||||
$mappingSource.RemoveAll()
|
||||
$mappingSource.SetAttribute("key", $Key)
|
||||
|
||||
foreach ($pattern in $Patterns) {
|
||||
$pkg = $Xml.CreateElement("package")
|
||||
$pkg.SetAttribute("pattern", $pattern)
|
||||
$mappingSource.AppendChild($pkg) | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
function Resolve-WinAppSdkSplitDependencies {
|
||||
Write-Host "Version $WinAppSDKVersion detected. Resolving split dependencies..."
|
||||
$installDir = Join-Path $rootPath "localpackages\output"
|
||||
New-Item -ItemType Directory -Path $installDir -Force | Out-Null
|
||||
|
||||
# Create a temporary nuget.config to avoid interference from the repo's config
|
||||
$tempConfig = Join-Path $env:TEMP "nuget_$(Get-Random).config"
|
||||
Set-Content -Path $tempConfig -Value "<?xml version='1.0' encoding='utf-8'?><configuration><packageSources><clear /><add key='TempSource' value='$sourceLink' /></packageSources></configuration>"
|
||||
|
||||
try {
|
||||
# Extract BuildTools version from Directory.Packages.props to ensure we have the required version
|
||||
$dirPackagesProps = Join-Path $rootPath "Directory.Packages.props"
|
||||
if (Test-Path $dirPackagesProps) {
|
||||
$propsContent = Get-Content $dirPackagesProps -Raw
|
||||
if ($propsContent -match '<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="([^"]+)"') {
|
||||
$buildToolsVersion = $Matches[1]
|
||||
Write-Host "Downloading Microsoft.Windows.SDK.BuildTools version $buildToolsVersion..."
|
||||
$nugetArgsBuildTools = "install Microsoft.Windows.SDK.BuildTools -Version $buildToolsVersion -ConfigFile $tempConfig -OutputDirectory $installDir -NonInteractive -NoCache"
|
||||
Invoke-Expression "nuget $nugetArgsBuildTools" | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
# Download package to inspect nuspec and keep it for the build
|
||||
$nugetArgs = "install Microsoft.WindowsAppSDK -Version $WinAppSDKVersion -ConfigFile $tempConfig -OutputDirectory $installDir -NonInteractive -NoCache"
|
||||
Invoke-Expression "nuget $nugetArgs" | Out-Null
|
||||
|
||||
# Parse dependencies from the installed folders
|
||||
# Folder structure is typically {PackageId}.{Version}
|
||||
$directories = Get-ChildItem -Path $installDir -Directory
|
||||
$allLocalPackages = @()
|
||||
foreach ($dir in $directories) {
|
||||
# Match any package pattern: PackageId.Version
|
||||
if ($dir.Name -match "^(.+?)\.(\d+\..*)$") {
|
||||
$pkgId = $Matches[1]
|
||||
$pkgVer = $Matches[2]
|
||||
$allLocalPackages += $pkgId
|
||||
|
||||
$packageVersions[$pkgId] = $pkgVer
|
||||
Write-Host "Found dependency: $pkgId = $pkgVer"
|
||||
}
|
||||
}
|
||||
|
||||
# Update repo's nuget.config to use localpackages
|
||||
$nugetConfig = Join-Path $rootPath "nuget.config"
|
||||
$configData = Read-FileWithEncoding -Path $nugetConfig
|
||||
[xml]$xml = $configData.Content
|
||||
|
||||
Add-NuGetSourceAndMapping -Xml $xml -Key "localpackages" -Value $installDir -Patterns $allLocalPackages
|
||||
|
||||
$xml.Save($nugetConfig)
|
||||
Write-Host "Updated nuget.config with localpackages mapping."
|
||||
} catch {
|
||||
Write-Warning "Failed to resolve dependencies: $_"
|
||||
} finally {
|
||||
Remove-Item $tempConfig -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
# Execute nuget list and capture the output
|
||||
if ($useExperimentalVersion) {
|
||||
# The nuget list for experimental versions will cost more time
|
||||
@@ -112,56 +213,36 @@ if ($latestVersion) {
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Update packages.config files
|
||||
Get-ChildItem -Path $rootPath -Recurse packages.config | ForEach-Object {
|
||||
$file = Read-FileWithEncoding -Path $_.FullName
|
||||
$content = $file.Content
|
||||
if ($content -match 'package id="Microsoft.WindowsAppSDK"') {
|
||||
$newVersionString = 'package id="Microsoft.WindowsAppSDK" version="' + $WinAppSDKVersion + '"'
|
||||
$oldVersionString = 'package id="Microsoft.WindowsAppSDK" version="[-.0-9a-zA-Z]*"'
|
||||
$content = $content -replace $oldVersionString, $newVersionString
|
||||
Write-FileWithEncoding -Path $_.FullName -Content $content -Encoding $file.encoding
|
||||
Write-Host "Modified " $_.FullName
|
||||
}
|
||||
}
|
||||
# Resolve dependencies for 1.8+
|
||||
$packageVersions = @{ "Microsoft.WindowsAppSDK" = $WinAppSDKVersion }
|
||||
|
||||
Resolve-WinAppSdkSplitDependencies
|
||||
|
||||
# Update Directory.Packages.props file
|
||||
Get-ChildItem -Path $rootPath -Recurse "Directory.Packages.props" | ForEach-Object {
|
||||
$file = Read-FileWithEncoding -Path $_.FullName
|
||||
$content = $file.Content
|
||||
if ($content -match '<PackageVersion Include="Microsoft.WindowsAppSDK"') {
|
||||
$newVersionString = '<PackageVersion Include="Microsoft.WindowsAppSDK" Version="' + $WinAppSDKVersion + '" />'
|
||||
$oldVersionString = '<PackageVersion Include="Microsoft.WindowsAppSDK" Version="[-.0-9a-zA-Z]*" />'
|
||||
$content = $content -replace $oldVersionString, $newVersionString
|
||||
$isModified = $false
|
||||
|
||||
foreach ($pkgId in $packageVersions.Keys) {
|
||||
$ver = $packageVersions[$pkgId]
|
||||
# Escape dots in package ID for regex
|
||||
$pkgIdRegex = $pkgId -replace '\.', '\.'
|
||||
|
||||
$newVersionString = "<PackageVersion Include=""$pkgId"" Version=""$ver"" />"
|
||||
$oldVersionString = "<PackageVersion Include=""$pkgIdRegex"" Version=""[-.0-9a-zA-Z]*"" />"
|
||||
|
||||
if ($content -match "<PackageVersion Include=""$pkgIdRegex""") {
|
||||
# Update existing package
|
||||
if ($content -notmatch [regex]::Escape($newVersionString)) {
|
||||
$content = $content -replace $oldVersionString, $newVersionString
|
||||
$isModified = $true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($isModified) {
|
||||
Write-FileWithEncoding -Path $_.FullName -Content $content -Encoding $file.encoding
|
||||
Write-Host "Modified " $_.FullName
|
||||
}
|
||||
}
|
||||
|
||||
# Update .vcxproj files
|
||||
Get-ChildItem -Path $rootPath -Recurse *.vcxproj | ForEach-Object {
|
||||
$file = Read-FileWithEncoding -Path $_.FullName
|
||||
$content = $file.Content
|
||||
if ($content -match '\\Microsoft.WindowsAppSDK.') {
|
||||
$newVersionString = '\Microsoft.WindowsAppSDK.' + $WinAppSDKVersion
|
||||
$oldVersionString = '\\Microsoft.WindowsAppSDK.(?=[-.0-9a-zA-Z]*\d)[-.0-9a-zA-Z]*' #positive lookahead for at least a digit
|
||||
$content = $content -replace $oldVersionString, $newVersionString
|
||||
Write-FileWithEncoding -Path $_.FullName -Content $content -Encoding $file.encoding
|
||||
Write-Host "Modified " $_.FullName
|
||||
}
|
||||
}
|
||||
|
||||
# Update .csproj files
|
||||
Get-ChildItem -Path $rootPath -Recurse *.csproj | ForEach-Object {
|
||||
$file = Read-FileWithEncoding -Path $_.FullName
|
||||
$content = $file.Content
|
||||
if ($content -match 'PackageReference Include="Microsoft.WindowsAppSDK"') {
|
||||
$newVersionString = 'PackageReference Include="Microsoft.WindowsAppSDK" Version="'+ $WinAppSDKVersion + '"'
|
||||
$oldVersionString = 'PackageReference Include="Microsoft.WindowsAppSDK" Version="[-.0-9a-zA-Z]*"'
|
||||
$content = $content -replace $oldVersionString, $newVersionString
|
||||
Write-FileWithEncoding -Path $_.FullName -Content $content -Encoding $file.encoding
|
||||
Write-Host "Modified " $_.FullName
|
||||
}
|
||||
}
|
||||
|
||||
Update-NugetConfig
|
||||
|
||||
@@ -19,7 +19,7 @@ parameters:
|
||||
- name: enableMsBuildCaching
|
||||
type: boolean
|
||||
displayName: "Enable MSBuild Caching"
|
||||
default: true
|
||||
default: false
|
||||
- name: runTests
|
||||
type: boolean
|
||||
displayName: "Run Tests"
|
||||
@@ -33,7 +33,7 @@ parameters:
|
||||
default: true
|
||||
- name: winAppSDKVersionNumber
|
||||
type: string
|
||||
default: 1.7
|
||||
default: 1.8
|
||||
- name: useExperimentalVersion
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
@@ -129,7 +129,7 @@ jobs:
|
||||
MSBuildMainBuildTargets: Build
|
||||
${{ insert }}: ${{ parameters.variables }}
|
||||
${{ if eq(parameters.useLatestWinAppSDK, true) }}:
|
||||
RestoreAdditionalProjectSourcesArg: '/p:RestoreAdditionalProjectSources="$(Build.SourcesDirectory)\localpackages\NugetPackages"'
|
||||
RestoreAdditionalProjectSourcesArg: '/p:RestoreAdditionalProjectSources="$(Build.SourcesDirectory)\localpackages\NugetPackages" /p:IgnoreExperimentalWarnings=true'
|
||||
${{ else }}:
|
||||
RestoreAdditionalProjectSourcesArg: ''
|
||||
displayName: Build
|
||||
|
||||
@@ -19,48 +19,20 @@ steps:
|
||||
-useExperimentalVersion $${{ parameters.useExperimentalVersion }}
|
||||
-rootPath "$(build.sourcesdirectory)"
|
||||
|
||||
- script: echo $(WinAppSDKVersion)
|
||||
displayName: 'Display WinAppSDK Version Found'
|
||||
# - task: NuGetCommand@2
|
||||
# displayName: 'Restore NuGet packages (slnx)'
|
||||
# inputs:
|
||||
# command: 'restore'
|
||||
# feedsToUse: 'config'
|
||||
# nugetConfigPath: '$(build.sourcesdirectory)\nuget.config'
|
||||
# restoreSolution: '$(build.sourcesdirectory)\**\*.slnx'
|
||||
# includeNuGetOrg: false
|
||||
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: 'Download WindowsAppSDK'
|
||||
inputs:
|
||||
buildType: 'specific'
|
||||
project: '55e8140e-57ac-4e5f-8f9c-c7c15b51929d'
|
||||
definition: '104083'
|
||||
buildVersionToDownload: 'latestFromBranch'
|
||||
branchName: 'refs/heads/release/${{ parameters.versionNumber }}-stable'
|
||||
artifactName: 'WindowsAppSDK_Nuget_And_MSIX'
|
||||
targetPath: '$(Build.SourcesDirectory)\localpackages'
|
||||
|
||||
- script: dir $(Build.SourcesDirectory)\localpackages\NugetPackages
|
||||
displayName: 'List downloaded packages'
|
||||
|
||||
- task: NuGetCommand@2
|
||||
displayName: 'Install WindowsAppSDK'
|
||||
inputs:
|
||||
command: 'custom'
|
||||
arguments: >
|
||||
install "Microsoft.WindowsAppSDK"
|
||||
-Source "$(Build.SourcesDirectory)\localpackages\NugetPackages"
|
||||
-Version "$(WinAppSDKVersion)"
|
||||
-OutputDirectory "$(Build.SourcesDirectory)\localpackages\output"
|
||||
-FallbackSource "https://microsoft.pkgs.visualstudio.com/ProjectReunion/_packaging/Project.Reunion.nuget.internal/nuget/v3/index.json"
|
||||
|
||||
- task: NuGetCommand@2
|
||||
displayName: 'Restore NuGet packages'
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: 'Restore NuGet packages (dotnet)'
|
||||
inputs:
|
||||
command: 'restore'
|
||||
projects: '$(build.sourcesdirectory)\**\*.slnx'
|
||||
feedsToUse: 'config'
|
||||
nugetConfigPath: '$(build.sourcesdirectory)\nuget.config'
|
||||
restoreSolution: '$(build.sourcesdirectory)\**\*.sln'
|
||||
includeNuGetOrg: false
|
||||
|
||||
- task: NuGetCommand@2
|
||||
displayName: 'Restore NuGet packages (slnx)'
|
||||
inputs:
|
||||
command: 'restore'
|
||||
feedsToUse: 'config'
|
||||
nugetConfigPath: '$(build.sourcesdirectory)\nuget.config'
|
||||
restoreSolution: '$(build.sourcesdirectory)\**\*.slnx'
|
||||
includeNuGetOrg: false
|
||||
workingDirectory: '$(build.sourcesdirectory)'
|
||||
|
||||
@@ -42,6 +42,11 @@
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<!-- Make angle-bracket includes external and turn off code analysis for them -->
|
||||
<TreatAngleIncludeAsExternal>true</TreatAngleIncludeAsExternal>
|
||||
<ExternalWarningLevel>TurnOffAllWarnings</ExternalWarningLevel>
|
||||
<DisableAnalyzeExternal>true</DisableAnalyzeExternal>
|
||||
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<PrecompiledHeader Condition="'$(UsePrecompiledHeaders)' != 'false'">Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
@@ -111,13 +116,11 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Debug/Release props -->
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'"
|
||||
Label="Configuration">
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'"
|
||||
Label="Configuration">
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
|
||||
@@ -262,6 +262,7 @@ _If you want to find diagnostic data events in the source code, these two links
|
||||
</table>
|
||||
|
||||
### Command Palette
|
||||
|
||||
<table style="width:100%">
|
||||
<tr>
|
||||
<th>Event Name</th>
|
||||
@@ -315,6 +316,14 @@ _If you want to find diagnostic data events in the source code, these two links
|
||||
<td>Microsoft.PowerToys.CmdPalProcessStarted</td>
|
||||
<td>Triggered when the Command Palette process is started.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Microsoft.PowerToys.CmdPal_ExtensionInvoked</td>
|
||||
<td>Tracks extension usage including extension ID, command details, success status, and execution time.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Microsoft.PowerToys.CmdPal_SessionDuration</td>
|
||||
<td>Logs session metrics from launch to dismissal including duration, commands executed, pages visited, search queries, navigation depth, and errors.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### Crop And Lock
|
||||
|
||||
@@ -8,4 +8,24 @@
|
||||
<PropertyGroup Label="ManifestToolOverride">
|
||||
<ManifestTool Condition="Exists('$(WindowsSdkDir)bin\x64\mt.exe')">$(WindowsSdkDir)bin\x64\mt.exe</ManifestTool>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Auto-restore NuGet for native vcxproj (PackageReference) when building inside VS -->
|
||||
<Target Name="EnsureNuGetRestoreForVcxproj" BeforeTargets="PrepareForBuild" Condition="
|
||||
'$(BuildingInsideVisualStudio)' == 'true'
|
||||
and '$(DesignTimeBuild)' != 'true'
|
||||
and '$(RestoreInProgress)' != 'true'
|
||||
and '$(MSBuildProjectExtension)' == '.vcxproj'
|
||||
and '$(RestoreProjectStyle)' == 'PackageReference'
|
||||
and '$(MSBuildProjectExtensionsPath)' != ''
|
||||
and !Exists('$(MSBuildProjectExtensionsPath)project.assets.json')
|
||||
">
|
||||
|
||||
<Message Importance="normal" Text="NuGet assets missing for $(MSBuildProjectName); running Restore...; IntDir=$(IntDir); BaseIntermediateOutputPath=$(BaseIntermediateOutputPath)" />
|
||||
|
||||
<MSBuild Projects="$(MSBuildProjectFullPath)" Targets="Restore" Properties="RestoreInProgress=true" BuildInParallel="false" />
|
||||
</Target>
|
||||
|
||||
<PropertyGroup Condition="'$(IgnoreExperimentalWarnings)' == 'true'">
|
||||
<NoWarn>$(NoWarn);CS8305;SA1500;CA1852</NoWarn>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -7,6 +7,8 @@
|
||||
<PackageVersion Include="AdaptiveCards.ObjectModel.WinUI3" Version="2.0.0-beta" />
|
||||
<PackageVersion Include="AdaptiveCards.Rendering.WinUI3" Version="2.1.0-beta" />
|
||||
<PackageVersion Include="AdaptiveCards.Templating" Version="2.0.5" />
|
||||
<PackageVersion Include="boost" Version="1.87.0" TargetFramework="native" />
|
||||
<PackageVersion Include="boost_regex-vc143" Version="1.87.0" TargetFramework="native" />
|
||||
<PackageVersion Include="CommunityToolkit.Labs.WinUI.Controls.OpacityMaskView" Version="0.1.251101-build.2372" />
|
||||
<PackageVersion Include="Microsoft.Bot.AdaptiveExpressions.Core" Version="4.23.0" />
|
||||
<PackageVersion Include="Appium.WebDriver" Version="4.4.5" />
|
||||
@@ -70,10 +72,12 @@
|
||||
This is present due to a bug in CsWinRT where WPF projects cause the analyzer to fail.
|
||||
-->
|
||||
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.2.0" />
|
||||
<PackageVersion Include="Microsoft.Windows.ImplementationLibrary" Version="1.0.231216.1"/>
|
||||
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.6901" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.8.250907003" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK.AI" Version="1.8.37" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK.Runtime" Version="1.8.250907003" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.8.251106002" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK.Foundation" Version="1.8.251104000" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK.AI" Version="1.8.39" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK.Runtime" Version="1.8.251106002" />
|
||||
<PackageVersion Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="2.0.9" />
|
||||
<PackageVersion Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />
|
||||
<PackageVersion Include="ModernWpfUI" Version="0.9.4" />
|
||||
@@ -112,6 +116,7 @@
|
||||
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="22.0.13" />
|
||||
<PackageVersion Include="System.Management" Version="9.0.10" />
|
||||
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
|
||||
<PackageVersion Include="System.Numerics.Tensors" Version="9.0.11" />
|
||||
<PackageVersion Include="System.Private.Uri" Version="4.3.2" />
|
||||
<PackageVersion Include="System.Reactive" Version="6.0.1" />
|
||||
<PackageVersion Include="System.Runtime.Caching" Version="9.0.10" />
|
||||
|
||||
@@ -976,6 +976,14 @@
|
||||
<Project Path="src/modules/ZoomIt/ZoomItSettingsInterop/ZoomItSettingsInterop.vcxproj" Id="ca7d8106-30b9-4aec-9d05-b69b31b8c461" />
|
||||
</Folder>
|
||||
<Folder Name="/settings-ui/">
|
||||
<Project Path="src/settings-ui/QuickAccess.UI/PowerToys.QuickAccess.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/settings-ui/Settings.UI.Controls/Settings.UI.Controls.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/settings-ui/Settings.UI.Library/Settings.UI.Library.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
|
||||
@@ -36,5 +36,6 @@ namespace ManagedCommon
|
||||
PowerOCR,
|
||||
Workspaces,
|
||||
ZoomIt,
|
||||
GeneralSettings,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,6 +119,16 @@ namespace PowerToysSettings
|
||||
class HotkeyObject
|
||||
{
|
||||
public:
|
||||
HotkeyObject() :
|
||||
m_json(json::JsonObject())
|
||||
{
|
||||
m_json.SetNamedValue(L"win", json::value(false));
|
||||
m_json.SetNamedValue(L"ctrl", json::value(false));
|
||||
m_json.SetNamedValue(L"alt", json::value(false));
|
||||
m_json.SetNamedValue(L"shift", json::value(false));
|
||||
m_json.SetNamedValue(L"code", json::value(0));
|
||||
m_json.SetNamedValue(L"key", json::value(L""));
|
||||
}
|
||||
static HotkeyObject from_json(json::JsonObject json)
|
||||
{
|
||||
return HotkeyObject(std::move(json));
|
||||
|
||||
@@ -144,7 +144,7 @@ public sealed class AIServiceBatchIntegrationTests
|
||||
switch (format)
|
||||
{
|
||||
case PasteFormats.CustomTextTransformation:
|
||||
var transformResult = await services.CustomActionTransformService.TransformTextAsync(batchTestInput.Prompt, batchTestInput.Clipboard, CancellationToken.None, progress);
|
||||
var transformResult = await services.CustomActionTransformService.TransformAsync(batchTestInput.Prompt, batchTestInput.Clipboard, null, CancellationToken.None, progress);
|
||||
return DataPackageHelpers.CreateFromText(transformResult.Content ?? string.Empty);
|
||||
|
||||
case PasteFormats.KernelQuery:
|
||||
|
||||
@@ -225,6 +225,24 @@ internal static class DataPackageHelpers
|
||||
internal static async Task<string> GetHtmlContentAsync(this DataPackageView dataPackageView) =>
|
||||
dataPackageView.Contains(StandardDataFormats.Html) ? await dataPackageView.GetHtmlFormatAsync() : string.Empty;
|
||||
|
||||
internal static async Task<byte[]> GetImageAsPngBytesAsync(this DataPackageView dataPackageView)
|
||||
{
|
||||
var bitmap = await dataPackageView.GetImageContentAsync();
|
||||
if (bitmap == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using var pngStream = new InMemoryRandomAccessStream();
|
||||
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, pngStream);
|
||||
encoder.SetSoftwareBitmap(bitmap);
|
||||
await encoder.FlushAsync();
|
||||
|
||||
using var memoryStream = new MemoryStream();
|
||||
await pngStream.AsStreamForRead().CopyToAsync(memoryStream);
|
||||
return memoryStream.ToArray();
|
||||
}
|
||||
|
||||
internal static async Task<SoftwareBitmap> GetImageContentAsync(this DataPackageView dataPackageView)
|
||||
{
|
||||
using var stream = await dataPackageView.GetImageStreamAsync();
|
||||
|
||||
@@ -166,5 +166,8 @@ namespace AdvancedPaste.Helpers
|
||||
|
||||
[DllImport("Shlwapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
internal static extern HResult AssocQueryString(AssocF flags, AssocStr str, string pszAssoc, string pszExtra, [Out] StringBuilder pszOut, [In][Out] ref uint pcchOut);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern uint GetClipboardSequenceNumber();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ public enum PasteFormats
|
||||
CanPreview = true,
|
||||
SupportedClipboardFormats = ClipboardFormat.Image,
|
||||
IPCKey = AdvancedPasteAdditionalActions.PropertyNames.ImageToText,
|
||||
KernelFunctionDescription = "Takes an image in the clipboard and extracts all text from it using OCR.")]
|
||||
KernelFunctionDescription = "Takes an image from the clipboard and extracts text using OCR. This function is intended only for explicit text extraction or OCR requests.")]
|
||||
ImageToText,
|
||||
|
||||
[PasteFormatMetadata(
|
||||
@@ -118,8 +118,8 @@ public enum PasteFormats
|
||||
IconGlyph = "\uE945",
|
||||
RequiresAIService = true,
|
||||
CanPreview = true,
|
||||
SupportedClipboardFormats = ClipboardFormat.Text,
|
||||
KernelFunctionDescription = "Takes input instructions and transforms clipboard text (not TXT files) with these input instructions, putting the result back on the clipboard. This uses AI to accomplish the task.",
|
||||
SupportedClipboardFormats = ClipboardFormat.Text | ClipboardFormat.Image,
|
||||
KernelFunctionDescription = "Takes user instructions and applies them to the current clipboard content (text or image). Use this function for image analysis, description, or transformation tasks beyond simple OCR.",
|
||||
RequiresPrompt = true)]
|
||||
CustomTextTransformation,
|
||||
}
|
||||
|
||||
@@ -40,15 +40,15 @@ namespace AdvancedPaste.Services.CustomActions
|
||||
this.userSettings = userSettings;
|
||||
}
|
||||
|
||||
public async Task<CustomActionTransformResult> TransformTextAsync(string prompt, string inputText, CancellationToken cancellationToken, IProgress<double> progress)
|
||||
public async Task<CustomActionTransformResult> TransformAsync(string prompt, string inputText, byte[] imageBytes, CancellationToken cancellationToken, IProgress<double> progress)
|
||||
{
|
||||
var pasteConfig = userSettings?.PasteAIConfiguration;
|
||||
var providerConfig = BuildProviderConfig(pasteConfig);
|
||||
|
||||
return await TransformAsync(prompt, inputText, providerConfig, cancellationToken, progress);
|
||||
return await TransformAsync(prompt, inputText, imageBytes, providerConfig, cancellationToken, progress);
|
||||
}
|
||||
|
||||
private async Task<CustomActionTransformResult> TransformAsync(string prompt, string inputText, PasteAIConfig providerConfig, CancellationToken cancellationToken, IProgress<double> progress)
|
||||
private async Task<CustomActionTransformResult> TransformAsync(string prompt, string inputText, byte[] imageBytes, PasteAIConfig providerConfig, CancellationToken cancellationToken, IProgress<double> progress)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(providerConfig);
|
||||
|
||||
@@ -57,9 +57,9 @@ namespace AdvancedPaste.Services.CustomActions
|
||||
return new CustomActionTransformResult(string.Empty, AIServiceUsage.None);
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(inputText))
|
||||
if (string.IsNullOrWhiteSpace(inputText) && imageBytes is null)
|
||||
{
|
||||
Logger.LogWarning("Clipboard has no usable text data");
|
||||
Logger.LogWarning("Clipboard has no usable data");
|
||||
return new CustomActionTransformResult(string.Empty, AIServiceUsage.None);
|
||||
}
|
||||
|
||||
@@ -80,6 +80,8 @@ namespace AdvancedPaste.Services.CustomActions
|
||||
{
|
||||
Prompt = prompt,
|
||||
InputText = inputText,
|
||||
ImageBytes = imageBytes,
|
||||
ImageMimeType = imageBytes != null ? "image/png" : null,
|
||||
SystemPrompt = systemPrompt,
|
||||
};
|
||||
|
||||
|
||||
@@ -12,6 +12,6 @@ namespace AdvancedPaste.Services.CustomActions
|
||||
{
|
||||
public interface ICustomActionTransformService
|
||||
{
|
||||
Task<CustomActionTransformResult> TransformTextAsync(string prompt, string inputText, CancellationToken cancellationToken, IProgress<double> progress);
|
||||
Task<CustomActionTransformResult> TransformAsync(string prompt, string inputText, byte[] imageBytes, CancellationToken cancellationToken, IProgress<double> progress);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,10 @@ namespace AdvancedPaste.Services.CustomActions
|
||||
|
||||
public string InputText { get; init; }
|
||||
|
||||
public byte[] ImageBytes { get; init; }
|
||||
|
||||
public string ImageMimeType { get; init; }
|
||||
|
||||
public string SystemPrompt { get; init; }
|
||||
|
||||
public AIServiceUsage Usage { get; set; } = AIServiceUsage.None;
|
||||
|
||||
@@ -64,21 +64,13 @@ namespace AdvancedPaste.Services.CustomActions
|
||||
|
||||
var prompt = request.Prompt;
|
||||
var inputText = request.InputText;
|
||||
if (string.IsNullOrWhiteSpace(prompt) || string.IsNullOrWhiteSpace(inputText))
|
||||
var imageBytes = request.ImageBytes;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(prompt) || (string.IsNullOrWhiteSpace(inputText) && imageBytes is null))
|
||||
{
|
||||
throw new ArgumentException("Prompt and input text must be provided", nameof(request));
|
||||
throw new ArgumentException("Prompt and input content must be provided", nameof(request));
|
||||
}
|
||||
|
||||
var userMessageContent = $"""
|
||||
User instructions:
|
||||
{prompt}
|
||||
|
||||
Clipboard Content:
|
||||
{inputText}
|
||||
|
||||
Output:
|
||||
""";
|
||||
|
||||
var executionSettings = CreateExecutionSettings();
|
||||
var kernel = CreateKernel();
|
||||
var modelId = _config.Model;
|
||||
@@ -102,7 +94,32 @@ namespace AdvancedPaste.Services.CustomActions
|
||||
|
||||
var chatHistory = new ChatHistory();
|
||||
chatHistory.AddSystemMessage(systemPrompt);
|
||||
chatHistory.AddUserMessage(userMessageContent);
|
||||
|
||||
if (imageBytes != null)
|
||||
{
|
||||
var collection = new ChatMessageContentItemCollection();
|
||||
if (!string.IsNullOrWhiteSpace(inputText))
|
||||
{
|
||||
collection.Add(new TextContent($"Clipboard Content:\n{inputText}"));
|
||||
}
|
||||
|
||||
collection.Add(new ImageContent(imageBytes, request.ImageMimeType ?? "image/png"));
|
||||
collection.Add(new TextContent($"User instructions:\n{prompt}\n\nOutput:"));
|
||||
chatHistory.AddUserMessage(collection);
|
||||
}
|
||||
else
|
||||
{
|
||||
var userMessageContent = $"""
|
||||
User instructions:
|
||||
{prompt}
|
||||
|
||||
Clipboard Content:
|
||||
{inputText}
|
||||
|
||||
Output:
|
||||
""";
|
||||
chatHistory.AddUserMessage(userMessageContent);
|
||||
}
|
||||
|
||||
var response = await chatService.GetChatMessageContentAsync(chatHistory, executionSettings, kernel, cancellationToken);
|
||||
chatHistory.Add(response);
|
||||
|
||||
@@ -67,12 +67,36 @@ public abstract class KernelServiceBase(
|
||||
|
||||
LogResult(cacheUsed, isSavedQuery, kernel.GetOrAddActionChain(), usage);
|
||||
|
||||
var outputPackage = kernel.GetDataPackage();
|
||||
var hasUsableData = await outputPackage.GetView().HasUsableDataAsync();
|
||||
|
||||
if (kernel.GetLastError() is Exception ex)
|
||||
{
|
||||
throw ex;
|
||||
// If we have an error, but the AI provided a final text response, we can ignore the error (likely a tool failure that the AI handled).
|
||||
// However, if we have usable data (e.g. from a successful tool call before the error?), we might want to keep it?
|
||||
// In the case of ImageToText failure, outputPackage is empty (new DataPackage), hasUsableData is false.
|
||||
// So we check if there is a valid response in the chat history.
|
||||
var lastMessage = chatHistory.LastOrDefault();
|
||||
bool hasAssistantResponse = lastMessage != null && lastMessage.Role == AuthorRole.Assistant && !string.IsNullOrEmpty(lastMessage.Content);
|
||||
|
||||
if (!hasAssistantResponse && !hasUsableData)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
|
||||
// If we have a response or data, we log the error but proceed.
|
||||
Logger.LogWarning($"Kernel operation encountered an error but proceeded with available response/data: {ex.Message}");
|
||||
}
|
||||
|
||||
var outputPackage = kernel.GetDataPackage();
|
||||
if (!hasUsableData)
|
||||
{
|
||||
var lastMessage = chatHistory.LastOrDefault();
|
||||
if (lastMessage != null && lastMessage.Role == AuthorRole.Assistant && !string.IsNullOrEmpty(lastMessage.Content))
|
||||
{
|
||||
outputPackage = DataPackageHelpers.CreateFromText(lastMessage.Content);
|
||||
kernel.SetDataPackage(outputPackage);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(await outputPackage.GetView().HasUsableDataAsync()))
|
||||
{
|
||||
@@ -148,7 +172,21 @@ public abstract class KernelServiceBase(
|
||||
var systemPrompt = string.IsNullOrWhiteSpace(runtimeConfig.SystemPrompt) ? DefaultSystemPrompt : runtimeConfig.SystemPrompt;
|
||||
chatHistory.AddSystemMessage(systemPrompt);
|
||||
chatHistory.AddSystemMessage($"Available clipboard formats: {await kernel.GetDataFormatsAsync()}");
|
||||
chatHistory.AddUserMessage(prompt);
|
||||
|
||||
var imageBytes = await kernel.GetDataPackageView().GetImageAsPngBytesAsync();
|
||||
if (imageBytes != null)
|
||||
{
|
||||
var collection = new ChatMessageContentItemCollection
|
||||
{
|
||||
new TextContent(prompt),
|
||||
new ImageContent(imageBytes, "image/png"),
|
||||
};
|
||||
chatHistory.AddUserMessage(collection);
|
||||
}
|
||||
else
|
||||
{
|
||||
chatHistory.AddUserMessage(prompt);
|
||||
}
|
||||
|
||||
if (ShouldModerateAdvancedAI())
|
||||
{
|
||||
@@ -302,8 +340,16 @@ public abstract class KernelServiceBase(
|
||||
new ActionChainItem(PasteFormats.CustomTextTransformation, Arguments: new() { { PromptParameterName, fixedPrompt } }),
|
||||
async dataPackageView =>
|
||||
{
|
||||
var input = await dataPackageView.GetClipboardTextOrThrowAsync(kernel.GetCancellationToken());
|
||||
var result = await _customActionTransformService.TransformTextAsync(fixedPrompt, input, kernel.GetCancellationToken(), kernel.GetProgress());
|
||||
var imageBytes = await dataPackageView.GetImageAsPngBytesAsync();
|
||||
var input = await dataPackageView.GetTextOrHtmlTextAsync();
|
||||
|
||||
if (string.IsNullOrEmpty(input) && imageBytes == null)
|
||||
{
|
||||
// If we have no text and no image, try to get text via OCR or throw if nothing exists
|
||||
input = await dataPackageView.GetClipboardTextOrThrowAsync(kernel.GetCancellationToken());
|
||||
}
|
||||
|
||||
var result = await _customActionTransformService.TransformAsync(fixedPrompt, input, imageBytes, kernel.GetCancellationToken(), kernel.GetProgress());
|
||||
return DataPackageHelpers.CreateFromText(result?.Content ?? string.Empty);
|
||||
});
|
||||
|
||||
@@ -313,15 +359,22 @@ public abstract class KernelServiceBase(
|
||||
new ActionChainItem(format, Arguments: new() { { PromptParameterName, prompt } }),
|
||||
async dataPackageView =>
|
||||
{
|
||||
var input = await dataPackageView.GetClipboardTextOrThrowAsync(kernel.GetCancellationToken());
|
||||
string output = await GetPromptBasedOutput(format, prompt, input, kernel.GetCancellationToken(), kernel.GetProgress());
|
||||
var imageBytes = await dataPackageView.GetImageAsPngBytesAsync();
|
||||
var input = await dataPackageView.GetTextOrHtmlTextAsync();
|
||||
|
||||
if (string.IsNullOrEmpty(input) && imageBytes == null)
|
||||
{
|
||||
input = await dataPackageView.GetClipboardTextOrThrowAsync(kernel.GetCancellationToken());
|
||||
}
|
||||
|
||||
string output = await GetPromptBasedOutput(format, prompt, input, imageBytes, kernel.GetCancellationToken(), kernel.GetProgress());
|
||||
return DataPackageHelpers.CreateFromText(output);
|
||||
});
|
||||
|
||||
private async Task<string> GetPromptBasedOutput(PasteFormats format, string prompt, string input, CancellationToken cancellationToken, IProgress<double> progress) =>
|
||||
private async Task<string> GetPromptBasedOutput(PasteFormats format, string prompt, string input, byte[] imageBytes, CancellationToken cancellationToken, IProgress<double> progress) =>
|
||||
format switch
|
||||
{
|
||||
PasteFormats.CustomTextTransformation => (await _customActionTransformService.TransformTextAsync(prompt, input, cancellationToken, progress))?.Content ?? string.Empty,
|
||||
PasteFormats.CustomTextTransformation => (await _customActionTransformService.TransformAsync(prompt, input, imageBytes, cancellationToken, progress))?.Content ?? string.Empty,
|
||||
_ => throw new ArgumentException($"Unsupported format {format} for prompt transform", nameof(format)),
|
||||
};
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ public sealed class PasteFormatExecutor(IKernelService kernelService, ICustomAct
|
||||
pasteFormat.Format switch
|
||||
{
|
||||
PasteFormats.KernelQuery => await _kernelService.TransformClipboardAsync(pasteFormat.Prompt, clipboardData, pasteFormat.IsSavedQuery, cancellationToken, progress),
|
||||
PasteFormats.CustomTextTransformation => DataPackageHelpers.CreateFromText((await _customActionTransformService.TransformTextAsync(pasteFormat.Prompt, await clipboardData.GetClipboardTextOrThrowAsync(cancellationToken), cancellationToken, progress))?.Content ?? string.Empty),
|
||||
PasteFormats.CustomTextTransformation => DataPackageHelpers.CreateFromText((await _customActionTransformService.TransformAsync(pasteFormat.Prompt, await clipboardData.GetTextOrHtmlTextAsync(), await clipboardData.GetImageAsPngBytesAsync(), cancellationToken, progress))?.Content ?? string.Empty),
|
||||
_ => await TransformHelpers.TransformAsync(format, clipboardData, cancellationToken, progress),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ namespace AdvancedPaste.ViewModels
|
||||
private CancellationTokenSource _pasteActionCancellationTokenSource;
|
||||
|
||||
private string _currentClipboardHistoryId;
|
||||
private uint _lastClipboardSequenceNumber;
|
||||
private DateTimeOffset? _currentClipboardTimestamp;
|
||||
private ClipboardFormat _lastClipboardFormats = ClipboardFormat.None;
|
||||
private bool _clipboardHistoryUnavailableLogged;
|
||||
@@ -455,6 +456,7 @@ namespace AdvancedPaste.ViewModels
|
||||
{
|
||||
ResetClipboardPreview();
|
||||
_currentClipboardHistoryId = null;
|
||||
_lastClipboardSequenceNumber = 0;
|
||||
_currentClipboardTimestamp = null;
|
||||
_lastClipboardFormats = ClipboardFormat.None;
|
||||
return;
|
||||
@@ -477,6 +479,13 @@ namespace AdvancedPaste.ViewModels
|
||||
{
|
||||
bool clipboardChanged = formatsChanged;
|
||||
|
||||
var currentSequenceNumber = NativeMethods.GetClipboardSequenceNumber();
|
||||
if (_lastClipboardSequenceNumber != currentSequenceNumber)
|
||||
{
|
||||
clipboardChanged = true;
|
||||
_lastClipboardSequenceNumber = currentSequenceNumber;
|
||||
}
|
||||
|
||||
if (Clipboard.IsHistoryEnabled())
|
||||
{
|
||||
try
|
||||
|
||||
@@ -312,13 +312,39 @@ private:
|
||||
return false;
|
||||
}
|
||||
|
||||
void read_settings(PowerToysSettings::PowerToyValues& settings)
|
||||
void read_settings(PowerToysSettings::PowerToyValues& settings)
|
||||
{
|
||||
const auto settingsObject = settings.get_raw_json();
|
||||
|
||||
// Migrate Paste As Plain text shortcut
|
||||
Hotkey old_paste_as_plain_hotkey;
|
||||
bool old_data_migrated = migrate_data_and_remove_data_file(old_paste_as_plain_hotkey);
|
||||
|
||||
if (settingsObject.GetView().Size())
|
||||
{
|
||||
const auto propertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES);
|
||||
|
||||
m_is_advanced_ai_enabled = has_advanced_ai_provider(propertiesObject);
|
||||
|
||||
if (propertiesObject.HasKey(JSON_KEY_IS_AI_ENABLED))
|
||||
{
|
||||
m_is_ai_enabled = propertiesObject.GetNamedObject(JSON_KEY_IS_AI_ENABLED).GetNamedBoolean(JSON_KEY_VALUE, false);
|
||||
}
|
||||
else if (propertiesObject.HasKey(JSON_KEY_IS_OPEN_AI_ENABLED))
|
||||
{
|
||||
m_is_ai_enabled = propertiesObject.GetNamedObject(JSON_KEY_IS_OPEN_AI_ENABLED).GetNamedBoolean(JSON_KEY_VALUE, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_is_ai_enabled = false;
|
||||
}
|
||||
|
||||
if (propertiesObject.HasKey(JSON_KEY_SHOW_CUSTOM_PREVIEW))
|
||||
{
|
||||
m_preview_custom_format_output = propertiesObject.GetNamedObject(JSON_KEY_SHOW_CUSTOM_PREVIEW).GetNamedBoolean(JSON_KEY_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
if (old_data_migrated)
|
||||
{
|
||||
m_paste_as_plain_hotkey = old_paste_as_plain_hotkey;
|
||||
@@ -405,31 +431,6 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (settingsObject.GetView().Size())
|
||||
{
|
||||
const auto propertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES);
|
||||
|
||||
m_is_advanced_ai_enabled = has_advanced_ai_provider(propertiesObject);
|
||||
|
||||
if (propertiesObject.HasKey(JSON_KEY_IS_AI_ENABLED))
|
||||
{
|
||||
m_is_ai_enabled = propertiesObject.GetNamedObject(JSON_KEY_IS_AI_ENABLED).GetNamedBoolean(JSON_KEY_VALUE, false);
|
||||
}
|
||||
else if (propertiesObject.HasKey(JSON_KEY_IS_OPEN_AI_ENABLED))
|
||||
{
|
||||
m_is_ai_enabled = propertiesObject.GetNamedObject(JSON_KEY_IS_OPEN_AI_ENABLED).GetNamedBoolean(JSON_KEY_VALUE, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_is_ai_enabled = false;
|
||||
}
|
||||
|
||||
if (propertiesObject.HasKey(JSON_KEY_SHOW_CUSTOM_PREVIEW))
|
||||
{
|
||||
m_preview_custom_format_output = propertiesObject.GetNamedObject(JSON_KEY_SHOW_CUSTOM_PREVIEW).GetNamedBoolean(JSON_KEY_VALUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load the settings file.
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<PropertyGroup Label="NuGet">
|
||||
<!-- Tell NuGet this is PackageReference style -->
|
||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
||||
|
||||
<!-- Tell NuGet we're a native project -->
|
||||
<NuGetTargetMoniker>native,Version=v0.0</NuGetTargetMoniker>
|
||||
|
||||
<!-- Tell NuGet we target Windows (use your existing WindowsTargetPlatformVersion) -->
|
||||
<NuGetTargetPlatformIdentifier>Windows</NuGetTargetPlatformIdentifier>
|
||||
<NuGetTargetPlatformVersion>$(WindowsTargetPlatformVersion)</NuGetTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<CppWinRTOptimized>true</CppWinRTOptimized>
|
||||
<CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge>
|
||||
@@ -31,6 +33,11 @@
|
||||
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
|
||||
<EnablePreviewMsixTooling>true</EnablePreviewMsixTooling>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" GeneratePathProperty="true" />
|
||||
<PackageReference Include="Microsoft.Windows.CppWinRT" GeneratePathProperty="true" />
|
||||
<PackageReference Include="Microsoft.Windows.ImplementationLibrary" GeneratePathProperty="true" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
@@ -38,7 +45,6 @@
|
||||
<DesktopCompatible>true</DesktopCompatible>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<Import Project="..\..\..\..\deps\spdlog.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
@@ -118,9 +124,6 @@
|
||||
<WarnAsError>true</WarnAsError>
|
||||
</Midl>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\common\Display\Display.vcxproj">
|
||||
<Project>{caba8dfb-823b-4bf2-93ac-3f31984150d9}</Project>
|
||||
@@ -142,42 +145,5 @@
|
||||
<ResourceCompile Include="PowerToys.MeasureToolCore.rc" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" />
|
||||
</ImportGroup>
|
||||
<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.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets'))" />
|
||||
</Target>
|
||||
<Import Project="..\..\..\..\deps\spdlog.props" />
|
||||
</Project>
|
||||
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Web.WebView2" version="1.0.2903.40" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.SDK.BuildTools" version="10.0.26100.4188" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK" version="1.8.250907003" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Base" version="1.8.250831001" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Foundation" version="1.8.250906002" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.WinUI" version="1.8.250906003" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Runtime" version="1.8.250907003" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.DWrite" version="1.8.25090401" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.InteractiveExperiences" version="1.8.250906004" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Widgets" version="1.8.250904007" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.AI" version="1.8.37" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.SDK.BuildTools.MSIX" version="1.7.20250829.1" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -73,6 +73,13 @@
|
||||
<ProjectReference Include="..\..\..\common\interop\PowerToys.Interop.vcxproj" />
|
||||
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
|
||||
<ProjectReference Include="..\MeasureToolCore\PowerToys.MeasureToolCore.vcxproj" />
|
||||
<ProjectReference Include="..\MeasureToolCore\PowerToys.MeasureToolCore.vcxproj">
|
||||
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
||||
<BuildProject>true</BuildProject>
|
||||
</ProjectReference>
|
||||
<CsWinRTInputs Include="$(OutputPath)\PowerToys.MeasureToolCore.winmd" />
|
||||
<None Include="$(OutputPath)\PowerToys.MeasureToolCore.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<PropertyGroup Label="NuGet">
|
||||
<!-- Tell NuGet this is PackageReference style -->
|
||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
||||
|
||||
<!-- Tell NuGet we're a native project -->
|
||||
<NuGetTargetMoniker>native,Version=v0.0</NuGetTargetMoniker>
|
||||
|
||||
<!-- Tell NuGet we target Windows (use your existing WindowsTargetPlatformVersion) -->
|
||||
<NuGetTargetPlatformIdentifier>Windows</NuGetTargetPlatformIdentifier>
|
||||
<NuGetTargetPlatformVersion>$(WindowsTargetPlatformVersion)</NuGetTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>15.0</VCProjectVersion>
|
||||
<ProjectGuid>{e94fd11c-0591-456f-899f-efc0ca548336}</ProjectGuid>
|
||||
@@ -20,9 +23,12 @@
|
||||
<WindowsAppSdkBootstrapInitialize>false</WindowsAppSdkBootstrapInitialize>
|
||||
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
|
||||
<WindowsAppSDKVerifyTransitiveDependencies>false</WindowsAppSDKVerifyTransitiveDependencies>
|
||||
<!-- Force NuGet to treat this project strictly as packages.config style -->
|
||||
<RestoreProjectStyle>packages.config</RestoreProjectStyle>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" GeneratePathProperty="true"/>
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK.Foundation" GeneratePathProperty="true"/>
|
||||
<PackageReference Include="Microsoft.Windows.CppWinRT" GeneratePathProperty="true"/>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
@@ -127,18 +133,18 @@
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="FindMyMouse.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<!-- Deduplicate WindowsAppRuntimeAutoInitializer.cpp (added twice via transitive imports causing LNK4042). Remove all then add exactly once. -->
|
||||
<ItemGroup Condition="'$(PkgMicrosoft_WindowsAppSDK)'!=''">
|
||||
<!-- Remove any transitive inclusion first -->
|
||||
<ClCompile Remove="$(PkgMicrosoft_WindowsAppSDK)\include\WindowsAppRuntimeAutoInitializer.cpp" />
|
||||
<!-- Re-add once, but disable PCH because the SDK file doesn't include our pch.h -->
|
||||
<ClCompile Include="$(PkgMicrosoft_WindowsAppSDK)\include\WindowsAppRuntimeAutoInitializer.cpp">
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<Target Name="FixWinAppSDKAutoInitializer" BeforeTargets="ClCompile" AfterTargets="WindowsAppRuntimeAutoInitializer">
|
||||
<ItemGroup>
|
||||
<!-- Remove ALL injected versions of the file -->
|
||||
<ClCompile Remove="@(ClCompile)" Condition="'%(Filename)' == 'WindowsAppRuntimeAutoInitializer'" />
|
||||
|
||||
<!-- Add ONE copy back manually -->
|
||||
<ClCompile Include="$(PkgMicrosoft_WindowsAppSDK_Foundation)\include\WindowsAppRuntimeAutoInitializer.cpp">
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
<Target Name="RemoveManagedWebView2CoreFromNativeOutDir" AfterTargets="Build">
|
||||
<ItemGroup>
|
||||
<_ToDelete Include="$(OutDir)Microsoft.Web.WebView2.Core.dll" />
|
||||
@@ -148,38 +154,4 @@
|
||||
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<Import Project="..\..\..\..\deps\spdlog.props" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.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.Windows.CppWinRT.2.0.240111.5\\build\\native\\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.Windows.CppWinRT.2.0.240111.5\\build\\native\\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.Windows.CppWinRT.2.0.240111.5\\build\\native\\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.Windows.CppWinRT.2.0.240111.5\\build\\native\\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.Web.WebView2.1.0.2903.40\\build\\native\\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.Web.WebView2.1.0.2903.40\\build\\native\\Microsoft.Web.WebView2.targets'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.props'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.targets'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Base.1.8.250831001\\build\\native\\Microsoft.WindowsAppSDK.Base.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Base.1.8.250831001\\build\\native\\Microsoft.WindowsAppSDK.Base.props'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Base.1.8.250831001\\build\\native\\Microsoft.WindowsAppSDK.Base.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Base.1.8.250831001\\build\\native\\Microsoft.WindowsAppSDK.Base.targets'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\\build\\native\\Microsoft.WindowsAppSDK.Foundation.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\\build\\native\\Microsoft.WindowsAppSDK.Foundation.props'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\\build\\native\\Microsoft.WindowsAppSDK.Foundation.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\\build\\native\\Microsoft.WindowsAppSDK.Foundation.targets'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\\build\\native\\Microsoft.WindowsAppSDK.WinUI.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\\build\\native\\Microsoft.WindowsAppSDK.WinUI.props'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\\build\\native\\Microsoft.WindowsAppSDK.WinUI.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\\build\\native\\Microsoft.WindowsAppSDK.WinUI.targets'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.Runtime.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.Runtime.props'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.Runtime.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.Runtime.targets'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\\build\\Microsoft.WindowsAppSDK.DWrite.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\\build\\Microsoft.WindowsAppSDK.DWrite.props'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\\build\\Microsoft.WindowsAppSDK.DWrite.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\\build\\Microsoft.WindowsAppSDK.DWrite.targets'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\\build\\native\\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\\build\\native\\Microsoft.WindowsAppSDK.InteractiveExperiences.props'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\\build\\native\\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\\build\\native\\Microsoft.WindowsAppSDK.InteractiveExperiences.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK" version="1.8.250907003" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Base" version="1.8.250831001" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Foundation" version="1.8.250906002" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.WinUI" version="1.8.250906003" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Runtime" version="1.8.250907003" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.DWrite" version="1.8.25090401" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.InteractiveExperiences" version="1.8.250906004" targetFramework="native" />
|
||||
<package id="Microsoft.Web.WebView2" version="1.0.2903.40" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.CmdPal.Core.ViewModels.Messages;
|
||||
|
||||
/// <summary>
|
||||
/// Message sent when an error occurs during command execution.
|
||||
/// Used to track session error count for telemetry.
|
||||
/// </summary>
|
||||
public record ErrorOccurredMessage();
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.CmdPal.Core.ViewModels.Messages;
|
||||
|
||||
/// <summary>
|
||||
/// Message sent when an extension command or page is invoked.
|
||||
/// Captures extension usage metrics for telemetry tracking.
|
||||
/// </summary>
|
||||
public record ExtensionInvokedMessage(string ExtensionId, string CommandId, string CommandName, bool Success, ulong ExecutionTimeMs);
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.CmdPal.Core.ViewModels.Messages;
|
||||
|
||||
/// <summary>
|
||||
/// Message containing the current navigation depth (BackStack count) when navigating to a page.
|
||||
/// Used to track maximum navigation depth reached during a session for telemetry.
|
||||
/// </summary>
|
||||
public record NavigationDepthMessage(int Depth);
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.CmdPal.Core.ViewModels.Messages;
|
||||
|
||||
/// <summary>
|
||||
/// Message sent when a search query is executed in the Command Palette.
|
||||
/// Used to track session search activity for telemetry.
|
||||
/// </summary>
|
||||
public record SearchQueryMessage();
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.CmdPal.Core.ViewModels.Messages;
|
||||
|
||||
/// <summary>
|
||||
/// Message containing session telemetry data from Command Palette launch to dismissal.
|
||||
/// Used to aggregate metrics like duration, commands executed, pages visited, and search activity.
|
||||
/// </summary>
|
||||
public record SessionDurationMessage(ulong DurationMs, int CommandsExecuted, int PagesVisited, string DismissalReason, int SearchQueriesCount, int MaxNavigationDepth, int ErrorCount);
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.CmdPal.Core.ViewModels.Messages;
|
||||
|
||||
/// <summary>
|
||||
/// Telemetry message sent when command invocation begins.
|
||||
/// </summary>
|
||||
public record TelemetryBeginInvokeMessage;
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.CmdPal.Core.ViewModels.Messages;
|
||||
|
||||
/// <summary>
|
||||
/// Telemetry message sent when an extension command or page is invoked.
|
||||
/// Captures extension usage metrics for telemetry tracking.
|
||||
/// </summary>
|
||||
public record TelemetryExtensionInvokedMessage(string ExtensionId, string CommandId, string CommandName, bool Success, ulong ExecutionTimeMs);
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.CmdPal.Core.ViewModels.Messages;
|
||||
|
||||
/// <summary>
|
||||
/// Telemetry message sent when command invocation completes with a result.
|
||||
/// </summary>
|
||||
public record TelemetryInvokeResultMessage(Microsoft.CommandPalette.Extensions.CommandResultKind Kind);
|
||||
@@ -270,8 +270,18 @@ public partial class ShellViewModel : ObservableObject,
|
||||
var isMainPage = command == _rootPage;
|
||||
_isNested = !isMainPage;
|
||||
|
||||
// Telemetry: Track extension page navigation for session metrics
|
||||
if (host is not null)
|
||||
{
|
||||
string extensionId = host.GetExtensionDisplayName() ?? "builtin";
|
||||
string commandId = command?.Id ?? "unknown";
|
||||
string commandName = command?.Name ?? "unknown";
|
||||
WeakReferenceMessenger.Default.Send<TelemetryExtensionInvokedMessage>(
|
||||
new(extensionId, commandId, commandName, true, 0));
|
||||
}
|
||||
|
||||
// Construct our ViewModel of the appropriate type and pass it the UI Thread context.
|
||||
var pageViewModel = _pageViewModelFactory.TryCreatePageViewModel(page, _isNested, host);
|
||||
var pageViewModel = _pageViewModelFactory.TryCreatePageViewModel(page, _isNested, host!);
|
||||
if (pageViewModel is null)
|
||||
{
|
||||
CoreLogger.LogError($"Failed to create ViewModel for page {page.GetType().Name}");
|
||||
@@ -306,7 +316,7 @@ public partial class ShellViewModel : ObservableObject,
|
||||
{
|
||||
CoreLogger.LogDebug($"Invoking command");
|
||||
|
||||
WeakReferenceMessenger.Default.Send<BeginInvokeMessage>();
|
||||
WeakReferenceMessenger.Default.Send<TelemetryBeginInvokeMessage>();
|
||||
StartInvoke(message, invokable, host);
|
||||
}
|
||||
}
|
||||
@@ -339,6 +349,14 @@ public partial class ShellViewModel : ObservableObject,
|
||||
|
||||
private void SafeHandleInvokeCommandSynchronous(PerformCommandMessage message, IInvokableCommand invokable, AppExtensionHost? host)
|
||||
{
|
||||
// Telemetry: Track command execution time and success
|
||||
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
|
||||
var command = message.Command.Unsafe;
|
||||
string extensionId = host?.GetExtensionDisplayName() ?? "builtin";
|
||||
string commandId = command?.Id ?? "unknown";
|
||||
string commandName = command?.Name ?? "unknown";
|
||||
bool success = false;
|
||||
|
||||
try
|
||||
{
|
||||
// Call out to extension process.
|
||||
@@ -349,16 +367,28 @@ public partial class ShellViewModel : ObservableObject,
|
||||
// But if it did succeed, we need to handle the result.
|
||||
UnsafeHandleCommandResult(result);
|
||||
|
||||
success = true;
|
||||
_handleInvokeTask = null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
success = false;
|
||||
_handleInvokeTask = null;
|
||||
|
||||
// Telemetry: Track errors for session metrics
|
||||
WeakReferenceMessenger.Default.Send<ErrorOccurredMessage>(new());
|
||||
|
||||
// TODO: It would be better to do this as a page exception, rather
|
||||
// than a silent log message.
|
||||
host?.Log(ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Telemetry: Send extension invocation metrics (always sent, even on failure)
|
||||
stopwatch.Stop();
|
||||
WeakReferenceMessenger.Default.Send<TelemetryExtensionInvokedMessage>(
|
||||
new(extensionId, commandId, commandName, success, (ulong)stopwatch.ElapsedMilliseconds));
|
||||
}
|
||||
}
|
||||
|
||||
private void UnsafeHandleCommandResult(ICommandResult? result)
|
||||
@@ -372,7 +402,7 @@ public partial class ShellViewModel : ObservableObject,
|
||||
var kind = result.Kind;
|
||||
CoreLogger.LogDebug($"handling {kind.ToString()}");
|
||||
|
||||
WeakReferenceMessenger.Default.Send<CmdPalInvokeResultMessage>(new(kind));
|
||||
WeakReferenceMessenger.Default.Send<TelemetryInvokeResultMessage>(new(kind));
|
||||
switch (kind)
|
||||
{
|
||||
case CommandResultKind.Dismiss:
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<!-- For MVVM Toolkit Partial Properties/AOT support -->
|
||||
<LangVersion>preview</LangVersion>
|
||||
|
||||
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal\</OutputPath>
|
||||
<OutputPath>..\..\..\$(Platform)\$(Configuration)\WinUI3Apps\CmdPal\</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<!-- MRT from windows app sdk will search for a pri file with the same name of the module before defaulting to resources.pri -->
|
||||
<ProjectPriFileName>$(RootNamespace).pri</ProjectPriFileName>
|
||||
|
||||
|
||||
<!-- Disable SA1313 for Primary Constructor fields conflict https://learn.microsoft.com/dotnet/csharp/programming-guide/classes-and-structs/instance-constructors#primary-constructors -->
|
||||
<NoWarn>SA1313;</NoWarn>
|
||||
|
||||
@@ -42,5 +42,5 @@
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -34,6 +34,6 @@ public sealed partial class CommandPaletteHost : AppExtensionHost, IExtensionHos
|
||||
|
||||
public override string? GetExtensionDisplayName()
|
||||
{
|
||||
return Extension?.ExtensionDisplayName;
|
||||
return Extension?.ExtensionDisplayName ?? _builtInProvider?.DisplayName ?? _builtInProvider?.Id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<!-- Images -->
|
||||
<Content Include="$(SolutionDir)\src\modules\cmdpal\Microsoft.CmdPal.UI\Assets\$(CmdPalAssetSuffix)\**\*">
|
||||
<Content Include=".\Assets\$(CmdPalAssetSuffix)\**\*">
|
||||
<DeploymentContent>true</DeploymentContent>
|
||||
<Link>Assets\%(RecursiveDir)%(FileName)%(Extension)</Link>
|
||||
</Content>
|
||||
@@ -35,14 +35,10 @@
|
||||
|
||||
<!-- In the future, when we actually want to support "preview" and "canary",
|
||||
add a Package-Pre.appxmanifest, etc. -->
|
||||
<AppxManifest Include="Package.appxmanifest"
|
||||
Condition="'$(CommandPaletteBranding)'=='Release'" />
|
||||
<AppxManifest Include="Package.appxmanifest"
|
||||
Condition="'$(CommandPaletteBranding)'=='Preview'" />
|
||||
<AppxManifest Include="Package.appxmanifest"
|
||||
Condition="'$(CommandPaletteBranding)'=='Canary'" />
|
||||
<AppxManifest Include="Package-Dev.appxmanifest"
|
||||
Condition="'$(CommandPaletteBranding)'=='' or '$(CommandPaletteBranding)'=='Dev'" />
|
||||
<AppxManifest Include="Package.appxmanifest" Condition="'$(CommandPaletteBranding)'=='Release'" />
|
||||
<AppxManifest Include="Package.appxmanifest" Condition="'$(CommandPaletteBranding)'=='Preview'" />
|
||||
<AppxManifest Include="Package.appxmanifest" Condition="'$(CommandPaletteBranding)'=='Canary'" />
|
||||
<AppxManifest Include="Package-Dev.appxmanifest" Condition="'$(CommandPaletteBranding)'=='' or '$(CommandPaletteBranding)'=='Dev'" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal</OutputPath>
|
||||
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)\WinUI3Apps\CmdPal</OutputPath>
|
||||
<!-- Reset this because the Versioning task might have overwritten it before it knew about OutDir -->
|
||||
<AppxPackageDir>$(OutputPath)\AppPackages\</AppxPackageDir>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -379,6 +379,12 @@ public sealed partial class SearchBar : UserControl,
|
||||
if (CurrentPageViewModel is not null)
|
||||
{
|
||||
CurrentPageViewModel.SearchTextBox = FilterBox.Text;
|
||||
|
||||
// Telemetry: Track search query count for session metrics (only non-empty queries)
|
||||
if (!string.IsNullOrWhiteSpace(FilterBox.Text))
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send<SearchQueryMessage>(new());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Windows.Foundation;
|
||||
|
||||
using ToolkitStretchChild = CommunityToolkit.WinUI.Controls.StretchChild;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.Controls;
|
||||
|
||||
/// <summary>
|
||||
@@ -177,9 +179,9 @@ public sealed partial class WrapPanel : Panel
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating how to arrange child items
|
||||
/// </summary>
|
||||
public StretchChild StretchChild
|
||||
public ToolkitStretchChild StretchChild
|
||||
{
|
||||
get { return (StretchChild)GetValue(StretchChildProperty); }
|
||||
get { return (ToolkitStretchChild)GetValue(StretchChildProperty); }
|
||||
set { SetValue(StretchChildProperty, value); }
|
||||
}
|
||||
|
||||
@@ -190,9 +192,9 @@ public sealed partial class WrapPanel : Panel
|
||||
public static readonly DependencyProperty StretchChildProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(StretchChild),
|
||||
typeof(StretchChild),
|
||||
typeof(ToolkitStretchChild),
|
||||
typeof(WrapPanel),
|
||||
new PropertyMetadata(StretchChild.None, LayoutPropertyChanged));
|
||||
new PropertyMetadata(ToolkitStretchChild.None, LayoutPropertyChanged));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the IsFullLine attached dependency property.
|
||||
@@ -397,7 +399,7 @@ public sealed partial class WrapPanel : Panel
|
||||
Arrange(Children[i]);
|
||||
}
|
||||
|
||||
Arrange(Children[lastIndex], StretchChild == StretchChild.Last);
|
||||
Arrange(Children[lastIndex], StretchChild == ToolkitStretchChild.Last);
|
||||
|
||||
if (currentRow.ChildrenRects.Count > 0)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.Tracing;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using Microsoft.PowerToys.Telemetry.Events;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Tracks extension usage with extension name and invocation details.
|
||||
/// Purpose: Identify popular vs. unused plugins and track extension performance.
|
||||
/// </summary>
|
||||
[EventData]
|
||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
||||
public class CmdPalExtensionInvoked : EventBase, IEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the unique identifier of the extension provider.
|
||||
/// </summary>
|
||||
public string ExtensionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the non-localized identifier of the command being invoked.
|
||||
/// </summary>
|
||||
public string CommandId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the localized display name of the command being invoked.
|
||||
/// </summary>
|
||||
public string CommandName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the command executed successfully.
|
||||
/// </summary>
|
||||
public bool Success { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the execution time in milliseconds.
|
||||
/// </summary>
|
||||
public ulong ExecutionTimeMs { get; set; }
|
||||
|
||||
public CmdPalExtensionInvoked(string extensionId, string commandId, string commandName, bool success, ulong executionTimeMs)
|
||||
{
|
||||
EventName = "CmdPal_ExtensionInvoked";
|
||||
ExtensionId = extensionId;
|
||||
CommandId = commandId;
|
||||
CommandName = commandName;
|
||||
Success = success;
|
||||
ExecutionTimeMs = executionTimeMs;
|
||||
}
|
||||
|
||||
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
||||
}
|
||||
@@ -18,6 +18,7 @@ public class CmdPalInvokeResult : EventBase, IEvent
|
||||
|
||||
public CmdPalInvokeResult(CommandResultKind resultKind)
|
||||
{
|
||||
EventName = "CmdPal_InvokeResult";
|
||||
ResultKind = resultKind.ToString();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.Tracing;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using Microsoft.PowerToys.Telemetry.Events;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Tracks Command Palette session duration from launch to close.
|
||||
/// Purpose: Understand user engagement patterns - quick actions vs. browsing behavior.
|
||||
/// </summary>
|
||||
[EventData]
|
||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
||||
public class CmdPalSessionDuration : EventBase, IEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the session duration in milliseconds.
|
||||
/// </summary>
|
||||
public ulong DurationMs { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of commands executed during the session.
|
||||
/// </summary>
|
||||
public int CommandsExecuted { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of pages visited during the session.
|
||||
/// </summary>
|
||||
public int PagesVisited { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the reason for dismissal (Escape, LostFocus, Command, etc.).
|
||||
/// </summary>
|
||||
public string DismissalReason { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of search queries executed during the session.
|
||||
/// </summary>
|
||||
public int SearchQueriesCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum navigation depth reached during the session.
|
||||
/// </summary>
|
||||
public int MaxNavigationDepth { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of errors encountered during the session.
|
||||
/// </summary>
|
||||
public int ErrorCount { get; set; }
|
||||
|
||||
public CmdPalSessionDuration(ulong durationMs, int commandsExecuted, int pagesVisited, string dismissalReason, int searchQueriesCount, int maxNavigationDepth, int errorCount)
|
||||
{
|
||||
EventName = "CmdPal_SessionDuration";
|
||||
DurationMs = durationMs;
|
||||
CommandsExecuted = commandsExecuted;
|
||||
PagesVisited = pagesVisited;
|
||||
DismissalReason = dismissalReason;
|
||||
SearchQueriesCount = searchQueriesCount;
|
||||
MaxNavigationDepth = maxNavigationDepth;
|
||||
ErrorCount = errorCount;
|
||||
}
|
||||
|
||||
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
||||
}
|
||||
@@ -6,40 +6,78 @@ using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.CmdPal.Core.Common.Services;
|
||||
using Microsoft.CmdPal.Core.ViewModels.Messages;
|
||||
using Microsoft.CmdPal.UI.Events;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
|
||||
namespace Microsoft.CmdPal.UI;
|
||||
|
||||
/// <summary>
|
||||
/// TelemetryForwarder is responsible for forwarding telemetry events from the
|
||||
/// command palette core to PowerToys Telemetry.
|
||||
/// This allows us to emit telemetry events as messages from the core,
|
||||
/// and then handle them by logging to our PT telemetry provider.
|
||||
///
|
||||
/// We may in the future want to replace this with a more generic "ITelemetryService"
|
||||
/// or something similar, but this works for now.
|
||||
/// command palette to PowerToys Telemetry.
|
||||
/// Listens to telemetry-specific messages from the core layer and logs them to PowerToys telemetry.
|
||||
/// Also implements ITelemetryService for dependency injection in extensions.
|
||||
/// </summary>
|
||||
internal sealed class TelemetryForwarder :
|
||||
ITelemetryService,
|
||||
IRecipient<BeginInvokeMessage>,
|
||||
IRecipient<CmdPalInvokeResultMessage>
|
||||
IRecipient<TelemetryBeginInvokeMessage>,
|
||||
IRecipient<TelemetryInvokeResultMessage>,
|
||||
IRecipient<TelemetryExtensionInvokedMessage>
|
||||
{
|
||||
public TelemetryForwarder()
|
||||
{
|
||||
WeakReferenceMessenger.Default.Register<BeginInvokeMessage>(this);
|
||||
WeakReferenceMessenger.Default.Register<CmdPalInvokeResultMessage>(this);
|
||||
WeakReferenceMessenger.Default.Register<TelemetryBeginInvokeMessage>(this);
|
||||
WeakReferenceMessenger.Default.Register<TelemetryInvokeResultMessage>(this);
|
||||
WeakReferenceMessenger.Default.Register<TelemetryExtensionInvokedMessage>(this);
|
||||
}
|
||||
|
||||
public void Receive(CmdPalInvokeResultMessage message)
|
||||
{
|
||||
PowerToysTelemetry.Log.WriteEvent(new CmdPalInvokeResult(message.Kind));
|
||||
}
|
||||
|
||||
public void Receive(BeginInvokeMessage message)
|
||||
// Message handlers for telemetry events from core layer
|
||||
public void Receive(TelemetryBeginInvokeMessage message)
|
||||
{
|
||||
PowerToysTelemetry.Log.WriteEvent(new BeginInvoke());
|
||||
}
|
||||
|
||||
public void Receive(TelemetryInvokeResultMessage message)
|
||||
{
|
||||
PowerToysTelemetry.Log.WriteEvent(new CmdPalInvokeResult(message.Kind));
|
||||
}
|
||||
|
||||
public void Receive(TelemetryExtensionInvokedMessage message)
|
||||
{
|
||||
PowerToysTelemetry.Log.WriteEvent(new CmdPalExtensionInvoked(
|
||||
message.ExtensionId,
|
||||
message.CommandId,
|
||||
message.CommandName,
|
||||
message.Success,
|
||||
message.ExecutionTimeMs));
|
||||
|
||||
// Increment session counter for commands executed
|
||||
if (App.Current.AppWindow is MainWindow mainWindow)
|
||||
{
|
||||
mainWindow.IncrementCommandsExecuted();
|
||||
}
|
||||
}
|
||||
|
||||
// Static method for logging session duration from UI layer
|
||||
public static void LogSessionDuration(
|
||||
ulong durationMs,
|
||||
int commandsExecuted,
|
||||
int pagesVisited,
|
||||
string dismissalReason,
|
||||
int searchQueriesCount,
|
||||
int maxNavigationDepth,
|
||||
int errorCount)
|
||||
{
|
||||
PowerToysTelemetry.Log.WriteEvent(new CmdPalSessionDuration(
|
||||
durationMs,
|
||||
commandsExecuted,
|
||||
pagesVisited,
|
||||
dismissalReason,
|
||||
searchQueriesCount,
|
||||
maxNavigationDepth,
|
||||
errorCount));
|
||||
}
|
||||
|
||||
// ITelemetryService implementation for dependency injection in extensions
|
||||
public void LogRunQuery(string query, int resultCount, ulong durationMs)
|
||||
{
|
||||
PowerToysTelemetry.Log.WriteEvent(new CmdPalRunQuery(query, resultCount, durationMs));
|
||||
|
||||
@@ -52,6 +52,10 @@ public sealed partial class MainWindow : WindowEx,
|
||||
IRecipient<ShowWindowMessage>,
|
||||
IRecipient<HideWindowMessage>,
|
||||
IRecipient<QuitMessage>,
|
||||
IRecipient<NavigateToPageMessage>,
|
||||
IRecipient<NavigationDepthMessage>,
|
||||
IRecipient<SearchQueryMessage>,
|
||||
IRecipient<ErrorOccurredMessage>,
|
||||
IRecipient<DragStartedMessage>,
|
||||
IRecipient<DragCompletedMessage>,
|
||||
IDisposable
|
||||
@@ -75,6 +79,14 @@ public sealed partial class MainWindow : WindowEx,
|
||||
private bool _ignoreHotKeyWhenFullScreen = true;
|
||||
private bool _themeServiceInitialized;
|
||||
|
||||
// Session tracking for telemetry
|
||||
private Stopwatch? _sessionStopwatch;
|
||||
private int _sessionCommandsExecuted;
|
||||
private int _sessionPagesVisited;
|
||||
private int _sessionSearchQueriesCount;
|
||||
private int _sessionMaxNavigationDepth;
|
||||
private int _sessionErrorCount;
|
||||
|
||||
private DesktopAcrylicController? _acrylicController;
|
||||
private SystemBackdropConfiguration? _configurationSource;
|
||||
private TimeSpan _autoGoHomeInterval = Timeout.InfiniteTimeSpan;
|
||||
@@ -123,6 +135,10 @@ public sealed partial class MainWindow : WindowEx,
|
||||
WeakReferenceMessenger.Default.Register<QuitMessage>(this);
|
||||
WeakReferenceMessenger.Default.Register<ShowWindowMessage>(this);
|
||||
WeakReferenceMessenger.Default.Register<HideWindowMessage>(this);
|
||||
WeakReferenceMessenger.Default.Register<NavigateToPageMessage>(this);
|
||||
WeakReferenceMessenger.Default.Register<NavigationDepthMessage>(this);
|
||||
WeakReferenceMessenger.Default.Register<SearchQueryMessage>(this);
|
||||
WeakReferenceMessenger.Default.Register<ErrorOccurredMessage>(this);
|
||||
WeakReferenceMessenger.Default.Register<DragStartedMessage>(this);
|
||||
WeakReferenceMessenger.Default.Register<DragCompletedMessage>(this);
|
||||
|
||||
@@ -524,6 +540,11 @@ public sealed partial class MainWindow : WindowEx,
|
||||
{
|
||||
var settings = App.Current.Services.GetService<SettingsModel>()!;
|
||||
|
||||
// Start session tracking
|
||||
_sessionStopwatch = Stopwatch.StartNew();
|
||||
_sessionCommandsExecuted = 0;
|
||||
_sessionPagesVisited = 0;
|
||||
|
||||
ShowHwnd(message.Hwnd, settings.SummonOn);
|
||||
}
|
||||
|
||||
@@ -532,6 +553,7 @@ public sealed partial class MainWindow : WindowEx,
|
||||
// This might come in off the UI thread. Make sure to hop back.
|
||||
DispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
EndSession("Hide");
|
||||
HideWindow();
|
||||
});
|
||||
}
|
||||
@@ -551,10 +573,67 @@ public sealed partial class MainWindow : WindowEx,
|
||||
// This might come in off the UI thread. Make sure to hop back.
|
||||
DispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
EndSession("Dismiss");
|
||||
HideWindow();
|
||||
});
|
||||
}
|
||||
|
||||
// Session telemetry: Track metrics during the Command Palette session
|
||||
// These receivers increment counters that are sent when EndSession is called
|
||||
public void Receive(NavigateToPageMessage message)
|
||||
{
|
||||
_sessionPagesVisited++;
|
||||
}
|
||||
|
||||
public void Receive(NavigationDepthMessage message)
|
||||
{
|
||||
if (message.Depth > _sessionMaxNavigationDepth)
|
||||
{
|
||||
_sessionMaxNavigationDepth = message.Depth;
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(SearchQueryMessage message)
|
||||
{
|
||||
_sessionSearchQueriesCount++;
|
||||
}
|
||||
|
||||
public void Receive(ErrorOccurredMessage message)
|
||||
{
|
||||
_sessionErrorCount++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends the current telemetry session and emits the CmdPal_SessionDuration event.
|
||||
/// Aggregates all session metrics collected since ShowWindow and sends them to telemetry.
|
||||
/// </summary>
|
||||
/// <param name="dismissalReason">The reason the session ended (e.g., Dismiss, Hide, LostFocus).</param>
|
||||
private void EndSession(string dismissalReason)
|
||||
{
|
||||
if (_sessionStopwatch is not null)
|
||||
{
|
||||
_sessionStopwatch.Stop();
|
||||
TelemetryForwarder.LogSessionDuration(
|
||||
(ulong)_sessionStopwatch.ElapsedMilliseconds,
|
||||
_sessionCommandsExecuted,
|
||||
_sessionPagesVisited,
|
||||
dismissalReason,
|
||||
_sessionSearchQueriesCount,
|
||||
_sessionMaxNavigationDepth,
|
||||
_sessionErrorCount);
|
||||
_sessionStopwatch = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increments the session commands executed counter for telemetry.
|
||||
/// Called by TelemetryForwarder when an extension command is invoked.
|
||||
/// </summary>
|
||||
internal void IncrementCommandsExecuted()
|
||||
{
|
||||
_sessionCommandsExecuted++;
|
||||
}
|
||||
|
||||
private void HideWindow()
|
||||
{
|
||||
// Cloak our HWND to avoid all animations.
|
||||
@@ -764,6 +843,7 @@ public sealed partial class MainWindow : WindowEx,
|
||||
}
|
||||
|
||||
// This will DWM cloak our window:
|
||||
EndSession("LostFocus");
|
||||
HideWindow();
|
||||
|
||||
PowerToysTelemetry.Log.WriteEvent(new CmdPalDismissedOnLostFocus());
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<LangVersion>preview</LangVersion>
|
||||
|
||||
<Version>$(CmdPalVersion)</Version>
|
||||
|
||||
@@ -23,13 +23,14 @@
|
||||
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<UseWinRT>true</UseWinRT>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<!-- For debugging purposes, uncomment this block to enable AOT builds -->
|
||||
<!--<PropertyGroup>
|
||||
<!--<PropertyGroup>
|
||||
<EnableCmdPalAOT>true</EnableCmdPalAOT>
|
||||
<GeneratePackageLocally>true</GeneratePackageLocally>
|
||||
</PropertyGroup>-->
|
||||
</PropertyGroup>-->
|
||||
|
||||
<PropertyGroup Condition="'$(EnableCmdPalAOT)' == 'true'">
|
||||
<SelfContained>true</SelfContained>
|
||||
@@ -45,7 +46,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- This lets us actually reference types from Microsoft.Terminal.UI -->
|
||||
<!-- This lets us actually reference types from Microsoft.Terminal.UI and CmdPalKeyboardService -->
|
||||
<CsWinRTIncludes>Microsoft.Terminal.UI;CmdPalKeyboardService</CsWinRTIncludes>
|
||||
<CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir>
|
||||
</PropertyGroup>
|
||||
@@ -101,7 +102,7 @@
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" />
|
||||
<PackageReference Include="Microsoft.Xaml.Behaviors.WinUI.Managed" />
|
||||
<PackageReference Include="WinUIEx" />
|
||||
<PackageReference Include="WinUIEx" />
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
@@ -147,12 +148,16 @@
|
||||
<ProjectReference Include="..\ext\Microsoft.CmdPal.Ext.WindowWalker\Microsoft.CmdPal.Ext.WindowWalker.csproj" />
|
||||
<ProjectReference Include="..\ext\Microsoft.CmdPal.Ext.WinGet\Microsoft.CmdPal.Ext.WinGet.csproj" />
|
||||
|
||||
<ProjectReference Include="..\Microsoft.Terminal.UI\Microsoft.Terminal.UI.vcxproj">
|
||||
<ReferenceOutputAssembly>True</ReferenceOutputAssembly>
|
||||
<Private>True</Private>
|
||||
<CopyLocalSatelliteAssemblies>True</CopyLocalSatelliteAssemblies>
|
||||
<ProjectReference Include="$(ProjectDir)\..\Microsoft.Terminal.UI\Microsoft.Terminal.UI.vcxproj">
|
||||
<ReferenceOutputAssembly>False</ReferenceOutputAssembly>
|
||||
<BuildProject>True</BuildProject>
|
||||
</ProjectReference>
|
||||
|
||||
<!-- WinRT metadata reference -->
|
||||
<CsWinRTInputs Include="$(OutputPath)\Microsoft.Terminal.UI.winmd" />
|
||||
<!-- Native implementation DLL -->
|
||||
<None Include="$(OutputPath)\Microsoft.Terminal.UI.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<ProjectReference Include="..\CmdPalKeyboardService\CmdPalKeyboardService.vcxproj">
|
||||
<ReferenceOutputAssembly>True</ReferenceOutputAssembly>
|
||||
<Private>True</Private>
|
||||
|
||||
@@ -161,6 +161,9 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
|
||||
|
||||
PowerToysTelemetry.Log.WriteEvent(new OpenPage(RootFrame.BackStackDepth, message.Page.Id));
|
||||
|
||||
// Telemetry: Send navigation depth for session max depth tracking
|
||||
WeakReferenceMessenger.Default.Send(new NavigationDepthMessage(RootFrame.BackStackDepth));
|
||||
|
||||
if (!ViewModel.IsNested)
|
||||
{
|
||||
// todo BODGY
|
||||
|
||||
@@ -12,9 +12,9 @@ namespace winrt::Microsoft::Terminal::UI::implementation
|
||||
// Check if the code point is in the Private Use Area range used by Fluent UI icons.
|
||||
[[nodiscard]] constexpr bool _isFluentIconPua(const UChar32 cp) noexcept
|
||||
{
|
||||
static constexpr UChar32 _fluentIconsPrivateUseAreaStart = 0xE700;
|
||||
static constexpr UChar32 _fluentIconsPrivateUseAreaEnd = 0xF8FF;
|
||||
return cp >= _fluentIconsPrivateUseAreaStart && cp <= _fluentIconsPrivateUseAreaEnd;
|
||||
constexpr UChar32 fluentIconsPrivateUseAreaStart = 0xE700;
|
||||
constexpr UChar32 fluentIconsPrivateUseAreaEnd = 0xF8FF;
|
||||
return cp >= fluentIconsPrivateUseAreaStart && cp <= fluentIconsPrivateUseAreaEnd;
|
||||
}
|
||||
|
||||
// Determine if the given text (as a sequence of UChar code units) is emoji
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<PropertyGroup>
|
||||
<PathToRoot>..\..\..\..\</PathToRoot>
|
||||
<WasdkNuget>$(PathToRoot)packages\Microsoft.WindowsAppSDK.1.8.250907003</WasdkNuget>
|
||||
<PropertyGroup Label="NuGet">
|
||||
<!-- Tell NuGet this is PackageReference style -->
|
||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
||||
<!-- Tell NuGet we're a native project -->
|
||||
<NuGetTargetMoniker>native,Version=v0.0</NuGetTargetMoniker>
|
||||
<!-- Tell NuGet we target Windows (use your existing WindowsTargetPlatformVersion) -->
|
||||
<NuGetTargetPlatformIdentifier>Windows</NuGetTargetPlatformIdentifier>
|
||||
<NuGetTargetPlatformVersion>$(WindowsTargetPlatformVersion)</NuGetTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<CppWinRTOptimized>true</CppWinRTOptimized>
|
||||
<CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge>
|
||||
@@ -28,6 +27,11 @@
|
||||
<WindowsTargetPlatformVersion Condition=" '$(WindowsTargetPlatformVersion)' == '' ">10.0.26100.0</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformMinVersion>10.0.19041.0</WindowsTargetPlatformMinVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" GeneratePathProperty="true" />
|
||||
<PackageReference Include="Microsoft.Windows.CppWinRT" GeneratePathProperty="true" />
|
||||
<PackageReference Include="Microsoft.Windows.ImplementationLibrary" GeneratePathProperty="true" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|ARM64">
|
||||
@@ -47,10 +51,6 @@
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal</OutDir>
|
||||
<IntDir>obj\$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
@@ -200,43 +200,11 @@
|
||||
<Midl Include="FontIconGlyphClassifier.idl" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
<None Include="Microsoft.Terminal.UI.def" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<OutDir>..\..\..\..\$(Platform)\$(Configuration)\WinUI3Apps\CmdPal</OutDir>
|
||||
<IntDir>obj\$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('$(MSBuildThisFileDirectory)..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
</ImportGroup>
|
||||
<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('$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.props'))" />
|
||||
<Error Condition="!Exists('$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', 'Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- The packages.config acts as the global version for all of the NuGet packages contained within. -->
|
||||
<packages>
|
||||
<package id="Microsoft.Web.WebView2" version="1.0.2903.40" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK" version="1.8.250907003" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Base" version="1.8.250831001" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Foundation" version="1.8.250906002" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.WinUI" version="1.8.250906003" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Runtime" version="1.8.250907003" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.DWrite" version="1.8.25090401" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.InteractiveExperiences" version="1.8.250906004" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Widgets" version="1.8.250904007" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.AI" version="1.8.37" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.SDK.BuildTools.MSIX" version="1.7.20250829.1" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -3,10 +3,9 @@
|
||||
<Import Project="..\..\..\..\Common.Dotnet.AotCompatibility.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
|
||||
<RepoRoot>$(MSBuildThisFileDirectory)..\..\..\..\..\</RepoRoot>
|
||||
<WindowsSdkPackageVersion>10.0.26100.57</WindowsSdkPackageVersion>
|
||||
|
||||
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\Microsoft.CommandPalette.Extensions.Toolkit</OutputPath>
|
||||
<OutputPath>$(RepoRoot)$(Platform)\$(Configuration)\Microsoft.CommandPalette.Extensions.Toolkit</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
@@ -21,7 +20,7 @@
|
||||
<PropertyGroup Condition="'$(CIBuild)'=='true'">
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<DelaySign>true</DelaySign>
|
||||
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)..\..\..\..\..\.pipelines\272MSSharedLibSN2048.snk</AssemblyOriginatorKeyFile>
|
||||
<AssemblyOriginatorKeyFile>$(RepoRoot).pipelines\272MSSharedLibSN2048.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
@@ -47,11 +46,19 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.CommandPalette.Extensions\Microsoft.CommandPalette.Extensions.vcxproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ProjectReference Include="..\Microsoft.CommandPalette.Extensions\Microsoft.CommandPalette.Extensions.vcxproj">
|
||||
<ReferenceOutputAssembly>False</ReferenceOutputAssembly>
|
||||
<BuildProject>True</BuildProject>
|
||||
</ProjectReference>
|
||||
<CsWinRTInputs Include="$(RepoRoot)$(Platform)\$(Configuration)\Microsoft.CommandPalette.Extensions\Microsoft.CommandPalette.Extensions.winmd" />
|
||||
<!-- Native implementation DLL -->
|
||||
<None Include="$(RepoRoot)$(Platform)\$(Configuration)\Microsoft.CommandPalette.Extensions\Microsoft.CommandPalette.Extensions.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="$(SolutionDir)$(Platform)\$(Configuration)\Microsoft.CommandPalette.Extensions\Microsoft.CommandPalette.Extensions.winmd" Link="Microsoft.CommandPalette.Extensions.winmd" CopyToOutputDirectory="PreserveNewest" />
|
||||
<Content Include="$(RepoRoot)$(Platform)\$(Configuration)\Microsoft.CommandPalette.Extensions\Microsoft.CommandPalette.Extensions.winmd" Link="Microsoft.CommandPalette.Extensions.winmd" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<PathToRoot>..\..\..\..\..\</PathToRoot>
|
||||
<WasdkNuget>$(PathToRoot)packages\Microsoft.WindowsAppSDK.1.8.250907003</WasdkNuget>
|
||||
<CppWinRTNuget>$(PathToRoot)packages\Microsoft.Windows.CppWinRT.2.0.240111.5</CppWinRTNuget>
|
||||
<WindowsSdkBuildToolsNuget>$(PathToRoot)packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.6901</WindowsSdkBuildToolsNuget>
|
||||
<WebView2Nuget>$(PathToRoot)packages\Microsoft.Web.WebView2.1.0.2903.40</WebView2Nuget>
|
||||
<PropertyGroup Label="NuGet">
|
||||
<!-- Tell NuGet this is PackageReference style -->
|
||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
||||
<!-- Tell NuGet we're a native project -->
|
||||
<NuGetTargetMoniker>native,Version=v0.0</NuGetTargetMoniker>
|
||||
<!-- Tell NuGet we target Windows (use your existing WindowsTargetPlatformVersion) -->
|
||||
<NuGetTargetPlatformIdentifier>Windows</NuGetTargetPlatformIdentifier>
|
||||
<NuGetTargetPlatformVersion>$(WindowsTargetPlatformVersion)</NuGetTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.props')" />
|
||||
<Import Project="$(CppWinRTNuget)\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('$(CppWinRTNuget)\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<Import Project="$(WindowsSdkBuildToolsNuget)\build\Microsoft.Windows.SDK.BuildTools.props" Condition="Exists('$(WindowsSdkBuildToolsNuget)\build\Microsoft.Windows.SDK.BuildTools.props')" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<RepoRoot>$(MSBuildThisFileDirectory)..\..\..\..\..\</RepoRoot>
|
||||
<CppWinRTOptimized>true</CppWinRTOptimized>
|
||||
<CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge>
|
||||
<CppWinRTGenerateWindowsMetadata>true</CppWinRTGenerateWindowsMetadata>
|
||||
@@ -25,7 +25,13 @@
|
||||
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
|
||||
<WindowsTargetPlatformMinVersion>10.0.19041.0</WindowsTargetPlatformMinVersion>
|
||||
<WindowsTargetPlatformVersion>10.0.26100.0</WindowsTargetPlatformVersion>
|
||||
<WindowsAppSDKVerifyTransitiveDependencies>false</WindowsAppSDKVerifyTransitiveDependencies>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" GeneratePathProperty="true" />
|
||||
<PackageReference Include="Microsoft.Windows.CppWinRT" GeneratePathProperty="true" />
|
||||
<PackageReference Include="Microsoft.Windows.ImplementationLibrary" GeneratePathProperty="true" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|ARM64">
|
||||
@@ -45,10 +51,6 @@
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\Microsoft.CommandPalette.Extensions\</OutDir>
|
||||
<IntDir>obj\$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
@@ -153,7 +155,6 @@
|
||||
<Midl Include="Microsoft.CommandPalette.Extensions.idl" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
<None Include="Microsoft.CommandPalette.Extensions.def" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -161,23 +162,9 @@
|
||||
<DeploymentContent>false</DeploymentContent>
|
||||
</Text>
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<OutDir>$(RepoRoot)$(Platform)\$(Configuration)\Microsoft.CommandPalette.Extensions\</OutDir>
|
||||
<IntDir>obj\$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="$(WindowsSdkBuildToolsNuget)\build\Microsoft.Windows.SDK.BuildTools.targets" Condition="Exists('$(WindowsSdkBuildToolsNuget)\build\Microsoft.Windows.SDK.BuildTools.targets')" />
|
||||
<Import Project="$(CppWinRTNuget)\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('$(CppWinRTNuget)\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.targets')" />
|
||||
<Import Project="$(WebView2Nuget)\build\native\Microsoft.Web.WebView2.targets" Condition="Exists('$(WebView2Nuget)\build\native\Microsoft.Web.WebView2.targets')" />
|
||||
</ImportGroup>
|
||||
<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('$(WindowsSdkBuildToolsNuget)\build\Microsoft.Windows.SDK.BuildTools.props')" Text="$([System.String]::Format('$(ErrorText)', '$(WindowsSdkBuildToolsNuget)\build\Microsoft.Windows.SDK.BuildTools.props'))" />
|
||||
<Error Condition="!Exists('$(WindowsSdkBuildToolsNuget)\build\Microsoft.Windows.SDK.BuildTools.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(WindowsSdkBuildToolsNuget)\build\Microsoft.Windows.SDK.BuildTools.targets'))" />
|
||||
<Error Condition="!Exists('$(CppWinRTNuget)\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '$(CppWinRTNuget)\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('$(CppWinRTNuget)\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(CppWinRTNuget)\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
<Error Condition="!Exists('$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.props'))" />
|
||||
<Error Condition="!Exists('$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.targets'))" />
|
||||
<Error Condition="!Exists('$(WebView2Nuget)\build\native\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(WebView2Nuget)\build\native\Microsoft.Web.WebView2.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Web.WebView2" version="1.0.2903.40" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK" version="1.8.250907003" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Base" version="1.8.250831001" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Foundation" version="1.8.250906002" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.WinUI" version="1.8.250906003" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Runtime" version="1.8.250907003" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.DWrite" version="1.8.25090401" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.InteractiveExperiences" version="1.8.250906004" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Widgets" version="1.8.250904007" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.AI" version="1.8.37" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.SDK.BuildTools" version="10.0.26100.6901" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.SDK.BuildTools.MSIX" version="1.7.20250829.1" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -11,6 +11,7 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="..\editor\FancyZonesEditor\Utils\ParsingResult.cs" Link="ParsingResult.cs" />
|
||||
<Compile Include="..\FancyZonesEditorCommon\Data\CustomLayouts.cs" Link="CustomLayouts.cs" />
|
||||
<Compile Include="..\FancyZonesEditorCommon\Data\FancyZonesPaths.cs" Link="FancyZonesPaths.cs" />
|
||||
<Compile Include="..\FancyZonesEditorCommon\Data\EditorData`1.cs" Link="EditorData`1.cs" />
|
||||
<Compile Include="..\FancyZonesEditorCommon\Data\LayoutDefaultSettings.cs" Link="LayoutDefaultSettings.cs" />
|
||||
<Compile Include="..\FancyZonesEditorCommon\Utils\DashCaseNamingPolicy.cs" Link="DashCaseNamingPolicy.cs" />
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.Invocation;
|
||||
|
||||
using FancyZonesCLI;
|
||||
using FancyZonesCLI.CommandLine;
|
||||
|
||||
namespace FancyZonesCLI.CommandLine.Commands;
|
||||
|
||||
internal abstract class FancyZonesBaseCommand : Command
|
||||
{
|
||||
protected FancyZonesBaseCommand(string name, string description)
|
||||
: base(name, description)
|
||||
{
|
||||
this.SetHandler(InvokeInternal);
|
||||
}
|
||||
|
||||
protected abstract string Execute(InvocationContext context);
|
||||
|
||||
private void InvokeInternal(InvocationContext context)
|
||||
{
|
||||
Logger.LogInfo($"Executing command '{Name}'");
|
||||
|
||||
if (!FancyZonesCliGuards.IsFancyZonesRunning())
|
||||
{
|
||||
Logger.LogWarning($"Command '{Name}' blocked: FancyZones is not running");
|
||||
context.Console.Error.Write($"Error: FancyZones is not running. Start PowerToys (FancyZones) and retry.{Environment.NewLine}");
|
||||
context.ExitCode = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string output = Execute(context);
|
||||
context.ExitCode = 0;
|
||||
|
||||
Logger.LogInfo($"Command '{Name}' completed successfully");
|
||||
Logger.LogDebug($"Command '{Name}' output length: {output?.Length ?? 0}");
|
||||
|
||||
if (!string.IsNullOrEmpty(output))
|
||||
{
|
||||
context.Console.Out.Write(output);
|
||||
context.Console.Out.Write(Environment.NewLine);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Command '{Name}' failed", ex);
|
||||
context.Console.Error.Write($"Error: {ex.Message}{Environment.NewLine}");
|
||||
context.ExitCode = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.CommandLine.Invocation;
|
||||
using System.Globalization;
|
||||
|
||||
using FancyZonesCLI.Utils;
|
||||
using FancyZonesEditorCommon.Data;
|
||||
using FancyZonesEditorCommon.Utils;
|
||||
|
||||
namespace FancyZonesCLI.CommandLine.Commands;
|
||||
|
||||
internal sealed partial class GetActiveLayoutCommand : FancyZonesBaseCommand
|
||||
{
|
||||
public GetActiveLayoutCommand()
|
||||
: base("get-active-layout", "Show currently active layout")
|
||||
{
|
||||
AddAlias("active");
|
||||
}
|
||||
|
||||
protected override string Execute(InvocationContext context)
|
||||
{
|
||||
// Trigger FancyZones to save current monitor info and read it reliably.
|
||||
var editorParams = EditorParametersRefresh.ReadEditorParametersWithRefresh(
|
||||
() => NativeMethods.NotifyFancyZones(NativeMethods.WM_PRIV_SAVE_EDITOR_PARAMETERS));
|
||||
|
||||
if (editorParams.Monitors == null || editorParams.Monitors.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("Could not get current monitor information.");
|
||||
}
|
||||
|
||||
// Read applied layouts.
|
||||
var appliedLayouts = FancyZonesDataIO.ReadAppliedLayouts();
|
||||
|
||||
if (appliedLayouts.AppliedLayouts == null)
|
||||
{
|
||||
return "No layouts configured.";
|
||||
}
|
||||
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.AppendLine("\n=== Active FancyZones Layout(s) ===\n");
|
||||
|
||||
// Show only layouts for currently connected monitors.
|
||||
for (int i = 0; i < editorParams.Monitors.Count; i++)
|
||||
{
|
||||
var monitor = editorParams.Monitors[i];
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"Monitor {i + 1}: {monitor.Monitor}");
|
||||
|
||||
var matchedLayout = AppliedLayoutsHelper.FindLayoutForMonitor(
|
||||
appliedLayouts,
|
||||
monitor.Monitor,
|
||||
monitor.MonitorSerialNumber,
|
||||
monitor.MonitorNumber,
|
||||
monitor.VirtualDesktop);
|
||||
|
||||
if (matchedLayout.HasValue)
|
||||
{
|
||||
var layout = matchedLayout.Value.AppliedLayout;
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" Layout UUID: {layout.Uuid}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" Layout Type: {layout.Type}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" Zone Count: {layout.ZoneCount}");
|
||||
|
||||
if (layout.ShowSpacing)
|
||||
{
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" Spacing: {layout.Spacing}px");
|
||||
}
|
||||
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" Sensitivity Radius: {layout.SensitivityRadius}px");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.AppendLine(" No layout applied");
|
||||
}
|
||||
|
||||
if (i < editorParams.Monitors.Count - 1)
|
||||
{
|
||||
sb.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString().TrimEnd();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.CommandLine.Invocation;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
using FancyZonesEditorCommon.Data;
|
||||
using FancyZonesEditorCommon.Utils;
|
||||
|
||||
namespace FancyZonesCLI.CommandLine.Commands;
|
||||
|
||||
internal sealed partial class GetHotkeysCommand : FancyZonesBaseCommand
|
||||
{
|
||||
public GetHotkeysCommand()
|
||||
: base("get-hotkeys", "List all layout hotkeys")
|
||||
{
|
||||
AddAlias("hk");
|
||||
}
|
||||
|
||||
protected override string Execute(InvocationContext context)
|
||||
{
|
||||
var hotkeys = FancyZonesDataIO.ReadLayoutHotkeys();
|
||||
|
||||
if (hotkeys.LayoutHotkeys == null || hotkeys.LayoutHotkeys.Count == 0)
|
||||
{
|
||||
return "No hotkeys configured.";
|
||||
}
|
||||
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.AppendLine("=== Layout Hotkeys ===\n");
|
||||
sb.AppendLine("Press Win + Ctrl + Alt + <number> to switch layouts:\n");
|
||||
|
||||
foreach (var hotkey in hotkeys.LayoutHotkeys.OrderBy(h => h.Key))
|
||||
{
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" [{hotkey.Key}] => {hotkey.LayoutId}");
|
||||
}
|
||||
|
||||
return sb.ToString().TrimEnd();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.CommandLine.Invocation;
|
||||
using System.Globalization;
|
||||
using System.Text.Json;
|
||||
|
||||
using FancyZonesEditorCommon.Data;
|
||||
using FancyZonesEditorCommon.Utils;
|
||||
|
||||
namespace FancyZonesCLI.CommandLine.Commands;
|
||||
|
||||
internal sealed partial class GetLayoutsCommand : FancyZonesBaseCommand
|
||||
{
|
||||
public GetLayoutsCommand()
|
||||
: base("get-layouts", "List available layouts")
|
||||
{
|
||||
AddAlias("ls");
|
||||
}
|
||||
|
||||
protected override string Execute(InvocationContext context)
|
||||
{
|
||||
var sb = new System.Text.StringBuilder();
|
||||
|
||||
// Print template layouts.
|
||||
var templatesJson = FancyZonesDataIO.ReadLayoutTemplates();
|
||||
|
||||
if (templatesJson.LayoutTemplates != null)
|
||||
{
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"=== Built-in Template Layouts ({templatesJson.LayoutTemplates.Count} total) ===\n");
|
||||
|
||||
for (int i = 0; i < templatesJson.LayoutTemplates.Count; i++)
|
||||
{
|
||||
var template = templatesJson.LayoutTemplates[i];
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"[T{i + 1}] {template.Type}");
|
||||
sb.Append(CultureInfo.InvariantCulture, $" Zones: {template.ZoneCount}");
|
||||
if (template.ShowSpacing && template.Spacing > 0)
|
||||
{
|
||||
sb.Append(CultureInfo.InvariantCulture, $", Spacing: {template.Spacing}px");
|
||||
}
|
||||
|
||||
sb.AppendLine();
|
||||
sb.AppendLine();
|
||||
|
||||
// Draw visual preview.
|
||||
sb.Append(LayoutVisualizer.DrawTemplateLayout(template));
|
||||
|
||||
if (i < templatesJson.LayoutTemplates.Count - 1)
|
||||
{
|
||||
sb.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
sb.AppendLine("\n");
|
||||
}
|
||||
|
||||
// Print custom layouts.
|
||||
var customLayouts = FancyZonesDataIO.ReadCustomLayouts();
|
||||
|
||||
if (customLayouts.CustomLayouts != null)
|
||||
{
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"=== Custom Layouts ({customLayouts.CustomLayouts.Count} total) ===");
|
||||
|
||||
for (int i = 0; i < customLayouts.CustomLayouts.Count; i++)
|
||||
{
|
||||
var layout = customLayouts.CustomLayouts[i];
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"[{i + 1}] {layout.Name}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" UUID: {layout.Uuid}");
|
||||
sb.Append(CultureInfo.InvariantCulture, $" Type: {layout.Type}");
|
||||
|
||||
bool isCanvasLayout = false;
|
||||
if (layout.Info.ValueKind != JsonValueKind.Undefined && layout.Info.ValueKind != JsonValueKind.Null)
|
||||
{
|
||||
if (layout.Type == "grid" && layout.Info.TryGetProperty("rows", out var rows) && layout.Info.TryGetProperty("columns", out var cols))
|
||||
{
|
||||
sb.Append(CultureInfo.InvariantCulture, $" ({rows.GetInt32()}x{cols.GetInt32()} grid)");
|
||||
}
|
||||
else if (layout.Type == "canvas" && layout.Info.TryGetProperty("zones", out var zones))
|
||||
{
|
||||
sb.Append(CultureInfo.InvariantCulture, $" ({zones.GetArrayLength()} zones)");
|
||||
isCanvasLayout = true;
|
||||
}
|
||||
}
|
||||
|
||||
sb.AppendLine("\n");
|
||||
|
||||
// Draw visual preview.
|
||||
sb.Append(LayoutVisualizer.DrawCustomLayout(layout));
|
||||
|
||||
// Add note for canvas layouts.
|
||||
if (isCanvasLayout)
|
||||
{
|
||||
sb.AppendLine("\n Note: Canvas layout preview is approximate.");
|
||||
sb.AppendLine(" Open FancyZones Editor for precise zone boundaries.");
|
||||
}
|
||||
|
||||
if (i < customLayouts.CustomLayouts.Count - 1)
|
||||
{
|
||||
sb.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
sb.AppendLine("\nUse 'FancyZonesCLI.exe set-layout <UUID>' to apply a layout.");
|
||||
}
|
||||
|
||||
return sb.ToString().TrimEnd();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.CommandLine.Invocation;
|
||||
using System.Globalization;
|
||||
|
||||
using FancyZonesCLI.Utils;
|
||||
using FancyZonesEditorCommon.Data;
|
||||
using FancyZonesEditorCommon.Utils;
|
||||
|
||||
namespace FancyZonesCLI.CommandLine.Commands;
|
||||
|
||||
internal sealed partial class GetMonitorsCommand : FancyZonesBaseCommand
|
||||
{
|
||||
public GetMonitorsCommand()
|
||||
: base("get-monitors", "List monitors and FancyZones metadata")
|
||||
{
|
||||
AddAlias("m");
|
||||
}
|
||||
|
||||
protected override string Execute(InvocationContext context)
|
||||
{
|
||||
// Request FancyZones to save current monitor configuration and read it reliably.
|
||||
EditorParameters.ParamsWrapper editorParams;
|
||||
try
|
||||
{
|
||||
editorParams = EditorParametersRefresh.ReadEditorParametersWithRefresh(
|
||||
() => NativeMethods.NotifyFancyZones(NativeMethods.WM_PRIV_SAVE_EDITOR_PARAMETERS));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to read monitor information. {ex.Message}{Environment.NewLine}Note: Ensure FancyZones is running to get current monitor information.", ex);
|
||||
}
|
||||
|
||||
if (editorParams.Monitors == null || editorParams.Monitors.Count == 0)
|
||||
{
|
||||
return "No monitors found.";
|
||||
}
|
||||
|
||||
// Also read applied layouts to show which layout is active on each monitor.
|
||||
var appliedLayouts = FancyZonesDataIO.ReadAppliedLayouts();
|
||||
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"=== Monitors ({editorParams.Monitors.Count} total) ===");
|
||||
sb.AppendLine();
|
||||
|
||||
for (int i = 0; i < editorParams.Monitors.Count; i++)
|
||||
{
|
||||
var monitor = editorParams.Monitors[i];
|
||||
var monitorNum = i + 1;
|
||||
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"Monitor {monitorNum}:");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" Monitor: {monitor.Monitor}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" Monitor Instance: {monitor.MonitorInstanceId}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" Monitor Number: {monitor.MonitorNumber}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" Serial Number: {monitor.MonitorSerialNumber}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" Virtual Desktop: {monitor.VirtualDesktop}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" DPI: {monitor.Dpi}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" Resolution: {monitor.MonitorWidth}x{monitor.MonitorHeight}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" Work Area: {monitor.WorkAreaWidth}x{monitor.WorkAreaHeight}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" Position: ({monitor.LeftCoordinate}, {monitor.TopCoordinate})");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" Selected: {monitor.IsSelected}");
|
||||
|
||||
// Find matching applied layout for this monitor using EditorCommon's matching logic.
|
||||
if (appliedLayouts.AppliedLayouts != null)
|
||||
{
|
||||
var matchedLayout = AppliedLayoutsHelper.FindLayoutForMonitor(
|
||||
appliedLayouts,
|
||||
monitor.Monitor,
|
||||
monitor.MonitorSerialNumber,
|
||||
monitor.MonitorNumber,
|
||||
monitor.VirtualDesktop);
|
||||
|
||||
if (matchedLayout != null && matchedLayout.Value.AppliedLayout.Type != null)
|
||||
{
|
||||
sb.AppendLine();
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" Active Layout: {matchedLayout.Value.AppliedLayout.Type}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" Zone Count: {matchedLayout.Value.AppliedLayout.ZoneCount}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" Sensitivity Radius: {matchedLayout.Value.AppliedLayout.SensitivityRadius}px");
|
||||
if (!string.IsNullOrEmpty(matchedLayout.Value.AppliedLayout.Uuid) &&
|
||||
matchedLayout.Value.AppliedLayout.Uuid != "{00000000-0000-0000-0000-000000000000}")
|
||||
{
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" Layout UUID: {matchedLayout.Value.AppliedLayout.Uuid}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
return sb.ToString().TrimEnd();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.CommandLine.Invocation;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace FancyZonesCLI.CommandLine.Commands;
|
||||
|
||||
internal sealed partial class OpenEditorCommand : FancyZonesBaseCommand
|
||||
{
|
||||
public OpenEditorCommand()
|
||||
: base("open-editor", "Launch FancyZones layout editor")
|
||||
{
|
||||
AddAlias("e");
|
||||
}
|
||||
|
||||
protected override string Execute(InvocationContext context)
|
||||
{
|
||||
const string FancyZonesEditorToggleEventName = "Local\\FancyZones-ToggleEditorEvent-1e174338-06a3-472b-874d-073b21c62f14";
|
||||
|
||||
// Check if editor is already running
|
||||
var existingProcess = Process.GetProcessesByName("PowerToys.FancyZonesEditor").FirstOrDefault();
|
||||
if (existingProcess != null)
|
||||
{
|
||||
NativeMethods.SetForegroundWindow(existingProcess.MainWindowHandle);
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, FancyZonesEditorToggleEventName);
|
||||
eventHandle.Set();
|
||||
return string.Empty;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to request FancyZones Editor launch. {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.CommandLine.Invocation;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace FancyZonesCLI.CommandLine.Commands;
|
||||
|
||||
internal sealed partial class OpenSettingsCommand : FancyZonesBaseCommand
|
||||
{
|
||||
public OpenSettingsCommand()
|
||||
: base("open-settings", "Open FancyZones settings page")
|
||||
{
|
||||
AddAlias("settings");
|
||||
}
|
||||
|
||||
protected override string Execute(InvocationContext context)
|
||||
{
|
||||
// Check in the same directory as the CLI (typical for dev builds)
|
||||
var powertoysExe = Path.Combine(AppContext.BaseDirectory, "PowerToys.exe");
|
||||
if (!File.Exists(powertoysExe))
|
||||
{
|
||||
throw new FileNotFoundException("PowerToys.exe not found. Ensure PowerToys is installed, or run the CLI from the same folder as PowerToys.exe.", powertoysExe);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var process = Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = powertoysExe,
|
||||
Arguments = "--open-settings=FancyZones",
|
||||
UseShellExecute = false,
|
||||
});
|
||||
|
||||
if (process == null)
|
||||
{
|
||||
throw new InvalidOperationException("PowerToys.exe failed to start.");
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to open FancyZones Settings. {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.Invocation;
|
||||
|
||||
using FancyZonesEditorCommon.Data;
|
||||
using FancyZonesEditorCommon.Utils;
|
||||
|
||||
namespace FancyZonesCLI.CommandLine.Commands;
|
||||
|
||||
internal sealed partial class RemoveHotkeyCommand : FancyZonesBaseCommand
|
||||
{
|
||||
private readonly Argument<int> _key;
|
||||
|
||||
public RemoveHotkeyCommand()
|
||||
: base("remove-hotkey", "Remove hotkey assignment")
|
||||
{
|
||||
AddAlias("rhk");
|
||||
|
||||
_key = new Argument<int>("key", "Hotkey index (0-9)");
|
||||
AddArgument(_key);
|
||||
}
|
||||
|
||||
protected override string Execute(InvocationContext context)
|
||||
{
|
||||
// FancyZones running guard is handled by FancyZonesBaseCommand.
|
||||
int key = context.ParseResult.GetValueForArgument(_key);
|
||||
|
||||
var hotkeysWrapper = FancyZonesDataIO.ReadLayoutHotkeys();
|
||||
|
||||
if (hotkeysWrapper.LayoutHotkeys == null)
|
||||
{
|
||||
return "No hotkeys configured.";
|
||||
}
|
||||
|
||||
var hotkeysList = hotkeysWrapper.LayoutHotkeys;
|
||||
var removed = hotkeysList.RemoveAll(h => h.Key == key);
|
||||
if (removed == 0)
|
||||
{
|
||||
return $"No hotkey assigned to key {key}";
|
||||
}
|
||||
|
||||
// Save.
|
||||
var newWrapper = new LayoutHotkeys.LayoutHotkeysWrapper { LayoutHotkeys = hotkeysList };
|
||||
FancyZonesDataIO.WriteLayoutHotkeys(newWrapper);
|
||||
|
||||
// Notify FancyZones.
|
||||
NativeMethods.NotifyFancyZones(NativeMethods.WM_PRIV_LAYOUT_HOTKEYS_FILE_UPDATE);
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.Invocation;
|
||||
using System.Linq;
|
||||
|
||||
using FancyZonesEditorCommon.Data;
|
||||
using FancyZonesEditorCommon.Utils;
|
||||
|
||||
namespace FancyZonesCLI.CommandLine.Commands;
|
||||
|
||||
internal sealed partial class SetHotkeyCommand : FancyZonesBaseCommand
|
||||
{
|
||||
private readonly Argument<int> _key;
|
||||
private readonly Argument<string> _layout;
|
||||
|
||||
public SetHotkeyCommand()
|
||||
: base("set-hotkey", "Assign hotkey (0-9) to a custom layout")
|
||||
{
|
||||
AddAlias("shk");
|
||||
|
||||
_key = new Argument<int>("key", "Hotkey index (0-9)");
|
||||
_layout = new Argument<string>("layout", "Custom layout UUID");
|
||||
|
||||
AddArgument(_key);
|
||||
AddArgument(_layout);
|
||||
}
|
||||
|
||||
protected override string Execute(InvocationContext context)
|
||||
{
|
||||
// FancyZones running guard is handled by FancyZonesBaseCommand.
|
||||
int key = context.ParseResult.GetValueForArgument(_key);
|
||||
string layout = context.ParseResult.GetValueForArgument(_layout);
|
||||
|
||||
if (key < 0 || key > 9)
|
||||
{
|
||||
throw new InvalidOperationException("Key must be between 0 and 9.");
|
||||
}
|
||||
|
||||
// Editor only allows assigning hotkeys to existing custom layouts.
|
||||
var customLayouts = FancyZonesDataIO.ReadCustomLayouts();
|
||||
|
||||
CustomLayouts.CustomLayoutWrapper? matchedLayout = null;
|
||||
if (customLayouts.CustomLayouts != null)
|
||||
{
|
||||
foreach (var candidate in customLayouts.CustomLayouts)
|
||||
{
|
||||
if (candidate.Uuid.Equals(layout, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
matchedLayout = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!matchedLayout.HasValue)
|
||||
{
|
||||
throw new InvalidOperationException($"Layout '{layout}' is not a custom layout UUID.");
|
||||
}
|
||||
|
||||
string layoutName = matchedLayout.Value.Name;
|
||||
|
||||
var hotkeysWrapper = FancyZonesDataIO.ReadLayoutHotkeys();
|
||||
|
||||
var hotkeysList = hotkeysWrapper.LayoutHotkeys ?? new List<LayoutHotkeys.LayoutHotkeyWrapper>();
|
||||
|
||||
// Match editor behavior:
|
||||
// - One key maps to one layout
|
||||
// - One layout maps to at most one key
|
||||
hotkeysList.RemoveAll(h => h.Key == key);
|
||||
hotkeysList.RemoveAll(h => string.Equals(h.LayoutId, layout, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
// Add new hotkey.
|
||||
hotkeysList.Add(new LayoutHotkeys.LayoutHotkeyWrapper { Key = key, LayoutId = layout });
|
||||
|
||||
// Save.
|
||||
var newWrapper = new LayoutHotkeys.LayoutHotkeysWrapper { LayoutHotkeys = hotkeysList };
|
||||
FancyZonesDataIO.WriteLayoutHotkeys(newWrapper);
|
||||
|
||||
// Notify FancyZones.
|
||||
NativeMethods.NotifyFancyZones(NativeMethods.WM_PRIV_LAYOUT_HOTKEYS_FILE_UPDATE);
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,374 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.Invocation;
|
||||
using System.Globalization;
|
||||
|
||||
using FancyZonesCLI.Utils;
|
||||
using FancyZonesEditorCommon.Data;
|
||||
using FancyZonesEditorCommon.Utils;
|
||||
|
||||
namespace FancyZonesCLI.CommandLine.Commands;
|
||||
|
||||
internal sealed partial class SetLayoutCommand : FancyZonesBaseCommand
|
||||
{
|
||||
private static readonly string[] AliasesMonitor = ["--monitor", "-m"];
|
||||
private static readonly string[] AliasesAll = ["--all", "-a"];
|
||||
|
||||
private const string DefaultLayoutUuid = "{00000000-0000-0000-0000-000000000000}";
|
||||
|
||||
private readonly Argument<string> _layoutId;
|
||||
private readonly Option<int?> _monitor;
|
||||
private readonly Option<bool> _all;
|
||||
|
||||
public SetLayoutCommand()
|
||||
: base("set-layout", "Set layout by UUID or template name")
|
||||
{
|
||||
AddAlias("s");
|
||||
|
||||
_layoutId = new Argument<string>("layout", "Layout UUID or template type (e.g. focus, columns)");
|
||||
AddArgument(_layoutId);
|
||||
|
||||
_monitor = new Option<int?>(AliasesMonitor, "Apply to monitor N (1-based)");
|
||||
_monitor.AddValidator(result =>
|
||||
{
|
||||
if (result.Tokens.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int? monitor = result.GetValueOrDefault<int?>();
|
||||
if (monitor.HasValue && monitor.Value < 1)
|
||||
{
|
||||
result.ErrorMessage = "Monitor index must be >= 1.";
|
||||
}
|
||||
});
|
||||
|
||||
_all = new Option<bool>(AliasesAll, "Apply to all monitors");
|
||||
|
||||
AddOption(_monitor);
|
||||
AddOption(_all);
|
||||
|
||||
AddValidator(commandResult =>
|
||||
{
|
||||
int? monitor = commandResult.GetValueForOption(_monitor);
|
||||
bool all = commandResult.GetValueForOption(_all);
|
||||
|
||||
if (monitor.HasValue && all)
|
||||
{
|
||||
commandResult.ErrorMessage = "Cannot specify both --monitor and --all.";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected override string Execute(InvocationContext context)
|
||||
{
|
||||
// FancyZones running guard is handled by FancyZonesBaseCommand.
|
||||
string layout = context.ParseResult.GetValueForArgument(_layoutId);
|
||||
int? monitor = context.ParseResult.GetValueForOption(_monitor);
|
||||
bool all = context.ParseResult.GetValueForOption(_all);
|
||||
Logger.LogInfo($"SetLayout called with layout: '{layout}', monitor: {(monitor.HasValue ? monitor.Value.ToString(CultureInfo.InvariantCulture) : "<default>")}, all: {all}");
|
||||
|
||||
var (targetCustomLayout, targetTemplate) = ResolveTargetLayout(layout);
|
||||
|
||||
var editorParams = ReadEditorParametersWithRefresh();
|
||||
var appliedLayouts = FancyZonesDataIO.ReadAppliedLayouts();
|
||||
appliedLayouts.AppliedLayouts ??= new List<AppliedLayouts.AppliedLayoutWrapper>();
|
||||
|
||||
List<int> monitorsToUpdate = GetMonitorsToUpdate(editorParams, monitor, all);
|
||||
List<AppliedLayouts.AppliedLayoutWrapper> newLayouts = BuildNewLayouts(editorParams, monitorsToUpdate, targetCustomLayout, targetTemplate);
|
||||
var updatedLayouts = MergeWithHistoricalLayouts(appliedLayouts, newLayouts);
|
||||
|
||||
Logger.LogInfo($"Writing {updatedLayouts.AppliedLayouts?.Count ?? 0} layouts to file");
|
||||
FancyZonesDataIO.WriteAppliedLayouts(updatedLayouts);
|
||||
Logger.LogInfo($"Applied layouts file updated for {monitorsToUpdate.Count} monitor(s)");
|
||||
|
||||
NativeMethods.NotifyFancyZones(NativeMethods.WM_PRIV_APPLIED_LAYOUTS_FILE_UPDATE);
|
||||
Logger.LogInfo("FancyZones notified of layout change");
|
||||
|
||||
return BuildSuccessMessage(layout, monitor, all);
|
||||
}
|
||||
|
||||
private static string BuildSuccessMessage(string layout, int? monitor, bool all)
|
||||
{
|
||||
if (all)
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "Layout '{0}' applied to all monitors.", layout);
|
||||
}
|
||||
|
||||
if (monitor.HasValue)
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "Layout '{0}' applied to monitor {1}.", layout, monitor.Value);
|
||||
}
|
||||
|
||||
return string.Format(CultureInfo.InvariantCulture, "Layout '{0}' applied to monitor 1.", layout);
|
||||
}
|
||||
|
||||
private static (CustomLayouts.CustomLayoutWrapper? TargetCustomLayout, LayoutTemplates.TemplateLayoutWrapper? TargetTemplate) ResolveTargetLayout(string layout)
|
||||
{
|
||||
var customLayouts = FancyZonesDataIO.ReadCustomLayouts();
|
||||
CustomLayouts.CustomLayoutWrapper? targetCustomLayout = FindCustomLayout(customLayouts, layout);
|
||||
|
||||
LayoutTemplates.TemplateLayoutWrapper? targetTemplate = null;
|
||||
if (!targetCustomLayout.HasValue || string.IsNullOrEmpty(targetCustomLayout.Value.Uuid))
|
||||
{
|
||||
var templates = FancyZonesDataIO.ReadLayoutTemplates();
|
||||
targetTemplate = FindTemplate(templates, layout);
|
||||
|
||||
if (targetCustomLayout.HasValue && string.IsNullOrEmpty(targetCustomLayout.Value.Uuid))
|
||||
{
|
||||
targetCustomLayout = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!targetCustomLayout.HasValue && !targetTemplate.HasValue)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Layout '{layout}' not found{Environment.NewLine}" +
|
||||
"Tip: For templates, use the type name (e.g., 'focus', 'columns', 'rows', 'grid', 'priority-grid')" +
|
||||
$"{Environment.NewLine} For custom layouts, use the UUID from 'get-layouts'");
|
||||
}
|
||||
|
||||
return (targetCustomLayout, targetTemplate);
|
||||
}
|
||||
|
||||
private static CustomLayouts.CustomLayoutWrapper? FindCustomLayout(CustomLayouts.CustomLayoutListWrapper customLayouts, string layout)
|
||||
{
|
||||
if (customLayouts.CustomLayouts == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (var customLayout in customLayouts.CustomLayouts)
|
||||
{
|
||||
if (customLayout.Uuid.Equals(layout, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return customLayout;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static LayoutTemplates.TemplateLayoutWrapper? FindTemplate(LayoutTemplates.TemplateLayoutsListWrapper templates, string layout)
|
||||
{
|
||||
if (templates.LayoutTemplates == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (var template in templates.LayoutTemplates)
|
||||
{
|
||||
if (template.Type.Equals(layout, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return template;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static EditorParameters.ParamsWrapper ReadEditorParametersWithRefresh()
|
||||
{
|
||||
return EditorParametersRefresh.ReadEditorParametersWithRefresh(
|
||||
() => NativeMethods.NotifyFancyZones(NativeMethods.WM_PRIV_SAVE_EDITOR_PARAMETERS));
|
||||
}
|
||||
|
||||
private static List<int> GetMonitorsToUpdate(EditorParameters.ParamsWrapper editorParams, int? monitor, bool all)
|
||||
{
|
||||
var result = new List<int>();
|
||||
|
||||
if (all)
|
||||
{
|
||||
for (int i = 0; i < editorParams.Monitors.Count; i++)
|
||||
{
|
||||
result.Add(i);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
if (monitor.HasValue)
|
||||
{
|
||||
int monitorIndex = monitor.Value - 1; // Convert to 0-based.
|
||||
if (monitorIndex < 0 || monitorIndex >= editorParams.Monitors.Count)
|
||||
{
|
||||
throw new InvalidOperationException($"Monitor {monitor.Value} not found. Available monitors: 1-{editorParams.Monitors.Count}");
|
||||
}
|
||||
|
||||
result.Add(monitorIndex);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Default: first monitor.
|
||||
result.Add(0);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<AppliedLayouts.AppliedLayoutWrapper> BuildNewLayouts(
|
||||
EditorParameters.ParamsWrapper editorParams,
|
||||
List<int> monitorsToUpdate,
|
||||
CustomLayouts.CustomLayoutWrapper? targetCustomLayout,
|
||||
LayoutTemplates.TemplateLayoutWrapper? targetTemplate)
|
||||
{
|
||||
var newLayouts = new List<AppliedLayouts.AppliedLayoutWrapper>();
|
||||
|
||||
foreach (int monitorIndex in monitorsToUpdate)
|
||||
{
|
||||
var currentMonitor = editorParams.Monitors[monitorIndex];
|
||||
|
||||
var (layoutUuid, layoutType, showSpacing, spacing, zoneCount, sensitivityRadius) =
|
||||
GetLayoutSettings(targetCustomLayout, targetTemplate);
|
||||
|
||||
var deviceId = new AppliedLayouts.AppliedLayoutWrapper.DeviceIdWrapper
|
||||
{
|
||||
Monitor = currentMonitor.Monitor,
|
||||
MonitorInstance = currentMonitor.MonitorInstanceId,
|
||||
MonitorNumber = currentMonitor.MonitorNumber,
|
||||
SerialNumber = currentMonitor.MonitorSerialNumber,
|
||||
VirtualDesktop = currentMonitor.VirtualDesktop,
|
||||
};
|
||||
|
||||
newLayouts.Add(new AppliedLayouts.AppliedLayoutWrapper
|
||||
{
|
||||
Device = deviceId,
|
||||
AppliedLayout = new AppliedLayouts.AppliedLayoutWrapper.LayoutWrapper
|
||||
{
|
||||
Uuid = layoutUuid,
|
||||
Type = layoutType,
|
||||
ShowSpacing = showSpacing,
|
||||
Spacing = spacing,
|
||||
ZoneCount = zoneCount,
|
||||
SensitivityRadius = sensitivityRadius,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (newLayouts.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("Internal error - no monitors to update.");
|
||||
}
|
||||
|
||||
return newLayouts;
|
||||
}
|
||||
|
||||
private static (string LayoutUuid, string LayoutType, bool ShowSpacing, int Spacing, int ZoneCount, int SensitivityRadius) GetLayoutSettings(
|
||||
CustomLayouts.CustomLayoutWrapper? targetCustomLayout,
|
||||
LayoutTemplates.TemplateLayoutWrapper? targetTemplate)
|
||||
{
|
||||
if (targetCustomLayout.HasValue)
|
||||
{
|
||||
var customLayoutsSerializer = new CustomLayouts();
|
||||
string type = targetCustomLayout.Value.Type?.ToLowerInvariant() ?? string.Empty;
|
||||
|
||||
bool showSpacing = false;
|
||||
int spacing = 0;
|
||||
int zoneCount = 0;
|
||||
int sensitivityRadius = 20;
|
||||
|
||||
if (type == "canvas")
|
||||
{
|
||||
var info = customLayoutsSerializer.CanvasFromJsonElement(targetCustomLayout.Value.Info.GetRawText());
|
||||
zoneCount = info.Zones?.Count ?? 0;
|
||||
sensitivityRadius = info.SensitivityRadius;
|
||||
}
|
||||
else if (type == "grid")
|
||||
{
|
||||
var info = customLayoutsSerializer.GridFromJsonElement(targetCustomLayout.Value.Info.GetRawText());
|
||||
showSpacing = info.ShowSpacing;
|
||||
spacing = info.Spacing;
|
||||
sensitivityRadius = info.SensitivityRadius;
|
||||
|
||||
if (info.CellChildMap != null)
|
||||
{
|
||||
var uniqueZoneIds = new HashSet<int>();
|
||||
|
||||
for (int r = 0; r < info.CellChildMap.Length; r++)
|
||||
{
|
||||
int[] row = info.CellChildMap[r];
|
||||
if (row == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int c = 0; c < row.Length; c++)
|
||||
{
|
||||
uniqueZoneIds.Add(row[c]);
|
||||
}
|
||||
}
|
||||
|
||||
zoneCount = uniqueZoneIds.Count;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"Unsupported custom layout type '{targetCustomLayout.Value.Type}'.");
|
||||
}
|
||||
|
||||
return (
|
||||
targetCustomLayout.Value.Uuid,
|
||||
Constants.CustomLayoutJsonTag,
|
||||
ShowSpacing: showSpacing,
|
||||
Spacing: spacing,
|
||||
ZoneCount: zoneCount,
|
||||
SensitivityRadius: sensitivityRadius);
|
||||
}
|
||||
|
||||
if (targetTemplate.HasValue)
|
||||
{
|
||||
return (
|
||||
DefaultLayoutUuid,
|
||||
targetTemplate.Value.Type,
|
||||
targetTemplate.Value.ShowSpacing,
|
||||
targetTemplate.Value.Spacing,
|
||||
targetTemplate.Value.ZoneCount,
|
||||
targetTemplate.Value.SensitivityRadius);
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Internal error - no layout selected.");
|
||||
}
|
||||
|
||||
private static AppliedLayouts.AppliedLayoutsListWrapper MergeWithHistoricalLayouts(
|
||||
AppliedLayouts.AppliedLayoutsListWrapper existingLayouts,
|
||||
List<AppliedLayouts.AppliedLayoutWrapper> newLayouts)
|
||||
{
|
||||
var mergedLayoutsList = new List<AppliedLayouts.AppliedLayoutWrapper>();
|
||||
mergedLayoutsList.AddRange(newLayouts);
|
||||
|
||||
if (existingLayouts.AppliedLayouts != null)
|
||||
{
|
||||
foreach (var existingLayout in existingLayouts.AppliedLayouts)
|
||||
{
|
||||
bool isUpdated = false;
|
||||
|
||||
foreach (var newLayout in newLayouts)
|
||||
{
|
||||
if (AppliedLayoutsHelper.MatchesDevice(
|
||||
existingLayout.Device,
|
||||
newLayout.Device.Monitor,
|
||||
newLayout.Device.SerialNumber,
|
||||
newLayout.Device.MonitorNumber,
|
||||
newLayout.Device.VirtualDesktop))
|
||||
{
|
||||
isUpdated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isUpdated)
|
||||
{
|
||||
mergedLayoutsList.Add(existingLayout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new AppliedLayouts.AppliedLayoutsListWrapper
|
||||
{
|
||||
AppliedLayouts = mergedLayoutsList,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.CommandLine;
|
||||
using FancyZonesCLI.CommandLine.Commands;
|
||||
|
||||
namespace FancyZonesCLI.CommandLine;
|
||||
|
||||
internal static class FancyZonesCliCommandFactory
|
||||
{
|
||||
public static RootCommand CreateRootCommand()
|
||||
{
|
||||
var root = new RootCommand("FancyZones CLI - Command line interface for FancyZones");
|
||||
|
||||
root.AddCommand(new OpenEditorCommand());
|
||||
root.AddCommand(new GetMonitorsCommand());
|
||||
root.AddCommand(new GetLayoutsCommand());
|
||||
root.AddCommand(new GetActiveLayoutCommand());
|
||||
root.AddCommand(new SetLayoutCommand());
|
||||
root.AddCommand(new OpenSettingsCommand());
|
||||
root.AddCommand(new GetHotkeysCommand());
|
||||
root.AddCommand(new SetHotkeyCommand());
|
||||
root.AddCommand(new RemoveHotkeyCommand());
|
||||
|
||||
return root;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace FancyZonesCLI.CommandLine;
|
||||
|
||||
internal static class FancyZonesCliGuards
|
||||
{
|
||||
public static bool IsFancyZonesRunning()
|
||||
{
|
||||
try
|
||||
{
|
||||
return Process.GetProcessesByName("PowerToys.FancyZones").Length != 0;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.CommandLine;
|
||||
using System.Linq;
|
||||
|
||||
namespace FancyZonesCLI.CommandLine;
|
||||
|
||||
internal static class FancyZonesCliUsage
|
||||
{
|
||||
public static void PrintUsage()
|
||||
{
|
||||
Console.OutputEncoding = System.Text.Encoding.UTF8;
|
||||
Console.WriteLine("FancyZones CLI - Command line interface for FancyZones");
|
||||
Console.WriteLine();
|
||||
|
||||
var cmd = FancyZonesCliCommandFactory.CreateRootCommand();
|
||||
|
||||
Console.WriteLine("Usage: FancyZonesCLI [command] [options]");
|
||||
Console.WriteLine();
|
||||
|
||||
Console.WriteLine("Options:");
|
||||
foreach (var option in cmd.Options)
|
||||
{
|
||||
var aliases = string.Join(", ", option.Aliases);
|
||||
var description = option.Description ?? string.Empty;
|
||||
Console.WriteLine($" {aliases,-30} {description}");
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Commands:");
|
||||
foreach (var command in cmd.Subcommands)
|
||||
{
|
||||
if (command.IsHidden)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Format: "command-name <args>, alias"
|
||||
string argsLabel = string.Join(" ", command.Arguments.Select(a => $"<{a.Name}>"));
|
||||
string baseLabel = string.IsNullOrEmpty(argsLabel) ? command.Name : $"{command.Name} {argsLabel}";
|
||||
|
||||
// Find first alias (Aliases includes Name)
|
||||
string alias = command.Aliases.FirstOrDefault(a => !string.Equals(a, command.Name, StringComparison.OrdinalIgnoreCase));
|
||||
string label = string.IsNullOrEmpty(alias) ? baseLabel : $"{baseLabel}, {alias}";
|
||||
|
||||
var description = command.Description ?? string.Empty;
|
||||
Console.WriteLine($" {label,-30} {description}");
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Examples:");
|
||||
Console.WriteLine(" FancyZonesCLI --help");
|
||||
Console.WriteLine(" FancyZonesCLI --version");
|
||||
Console.WriteLine(" FancyZonesCLI get-monitors");
|
||||
Console.WriteLine(" FancyZonesCLI set-layout focus");
|
||||
Console.WriteLine(" FancyZonesCLI set-layout <uuid> --monitor 1");
|
||||
Console.WriteLine(" FancyZonesCLI get-hotkeys");
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace FancyZonesCLI.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Editor and Settings commands.
|
||||
/// </summary>
|
||||
internal static class EditorCommands
|
||||
{
|
||||
public static (int ExitCode, string Output) OpenEditor()
|
||||
{
|
||||
var editorExe = "PowerToys.FancyZonesEditor.exe";
|
||||
|
||||
// Check if editor-parameters.json exists
|
||||
if (!FancyZonesData.EditorParametersExist())
|
||||
{
|
||||
return (1, "Error: editor-parameters.json not found.\nPlease launch FancyZones Editor using Win+` (Win+Backtick) hotkey first.");
|
||||
}
|
||||
|
||||
// Check if editor is already running
|
||||
var existingProcess = Process.GetProcessesByName("PowerToys.FancyZonesEditor").FirstOrDefault();
|
||||
if (existingProcess != null)
|
||||
{
|
||||
NativeMethods.SetForegroundWindow(existingProcess.MainWindowHandle);
|
||||
return (0, "FancyZones Editor is already running. Brought window to foreground.");
|
||||
}
|
||||
|
||||
// Only check same directory as CLI
|
||||
var editorPath = Path.Combine(AppContext.BaseDirectory, editorExe);
|
||||
|
||||
if (File.Exists(editorPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = editorPath,
|
||||
UseShellExecute = true,
|
||||
});
|
||||
return (0, "FancyZones Editor launched successfully.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return (1, $"Failed to launch: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
return (1, $"Error: Could not find {editorExe} in {AppContext.BaseDirectory}");
|
||||
}
|
||||
|
||||
public static (int ExitCode, string Output) OpenSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Find PowerToys.exe in common locations
|
||||
string powertoysExe = null;
|
||||
|
||||
// Check in the same directory as the CLI (typical for dev builds)
|
||||
var sameDirPath = Path.Combine(AppContext.BaseDirectory, "PowerToys.exe");
|
||||
if (File.Exists(sameDirPath))
|
||||
{
|
||||
powertoysExe = sameDirPath;
|
||||
}
|
||||
|
||||
if (powertoysExe == null)
|
||||
{
|
||||
return (1, "Error: PowerToys.exe not found. Please ensure PowerToys is installed.");
|
||||
}
|
||||
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = powertoysExe,
|
||||
Arguments = "--open-settings=FancyZones",
|
||||
UseShellExecute = false,
|
||||
});
|
||||
return (0, "FancyZones Settings opened successfully.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return (1, $"Error: Failed to open FancyZones Settings. {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
namespace FancyZonesCLI.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Hotkey-related commands.
|
||||
/// </summary>
|
||||
internal static class HotkeyCommands
|
||||
{
|
||||
public static (int ExitCode, string Output) GetHotkeys()
|
||||
{
|
||||
var hotkeys = FancyZonesData.ReadLayoutHotkeys();
|
||||
if (hotkeys?.Hotkeys == null || hotkeys.Hotkeys.Count == 0)
|
||||
{
|
||||
return (0, "No hotkeys configured.");
|
||||
}
|
||||
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.AppendLine("=== Layout Hotkeys ===\n");
|
||||
sb.AppendLine("Press Win + Ctrl + Alt + <number> to switch layouts:\n");
|
||||
|
||||
foreach (var hotkey in hotkeys.Hotkeys.OrderBy(h => h.Key))
|
||||
{
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" [{hotkey.Key}] => {hotkey.LayoutId}");
|
||||
}
|
||||
|
||||
return (0, sb.ToString().TrimEnd());
|
||||
}
|
||||
|
||||
public static (int ExitCode, string Output) SetHotkey(int key, string layoutUuid, Action<uint> notifyFancyZones, uint wmPrivLayoutHotkeysFileUpdate)
|
||||
{
|
||||
if (key < 0 || key > 9)
|
||||
{
|
||||
return (1, "Error: Key must be between 0 and 9");
|
||||
}
|
||||
|
||||
// Check if this is a custom layout UUID
|
||||
var customLayouts = FancyZonesData.ReadCustomLayouts();
|
||||
var matchedLayout = customLayouts?.Layouts?.FirstOrDefault(l => l.Uuid.Equals(layoutUuid, StringComparison.OrdinalIgnoreCase));
|
||||
bool isCustomLayout = matchedLayout != null;
|
||||
string layoutName = matchedLayout?.Name ?? layoutUuid;
|
||||
|
||||
var hotkeys = FancyZonesData.ReadLayoutHotkeys() ?? new LayoutHotkeys();
|
||||
|
||||
hotkeys.Hotkeys ??= new List<LayoutHotkey>();
|
||||
|
||||
// Remove existing hotkey for this key
|
||||
hotkeys.Hotkeys.RemoveAll(h => h.Key == key);
|
||||
|
||||
// Add new hotkey
|
||||
hotkeys.Hotkeys.Add(new LayoutHotkey { Key = key, LayoutId = layoutUuid });
|
||||
|
||||
// Save
|
||||
FancyZonesData.WriteLayoutHotkeys(hotkeys);
|
||||
|
||||
// Notify FancyZones
|
||||
notifyFancyZones(wmPrivLayoutHotkeysFileUpdate);
|
||||
|
||||
if (isCustomLayout)
|
||||
{
|
||||
return (0, $"✓ Hotkey {key} assigned to custom layout '{layoutName}'\n Press Win + Ctrl + Alt + {key} to switch to this layout");
|
||||
}
|
||||
else
|
||||
{
|
||||
return (0, $"⚠ Warning: Hotkey {key} assigned to '{layoutUuid}'\n Note: FancyZones hotkeys only work with CUSTOM layouts.\n Template layouts (focus, columns, rows, etc.) cannot be used with hotkeys.\n Create a custom layout in the FancyZones Editor to use this hotkey.");
|
||||
}
|
||||
}
|
||||
|
||||
public static (int ExitCode, string Output) RemoveHotkey(int key, Action<uint> notifyFancyZones, uint wmPrivLayoutHotkeysFileUpdate)
|
||||
{
|
||||
var hotkeys = FancyZonesData.ReadLayoutHotkeys();
|
||||
if (hotkeys?.Hotkeys == null)
|
||||
{
|
||||
return (0, $"No hotkey assigned to key {key}");
|
||||
}
|
||||
|
||||
var removed = hotkeys.Hotkeys.RemoveAll(h => h.Key == key);
|
||||
if (removed == 0)
|
||||
{
|
||||
return (0, $"No hotkey assigned to key {key}");
|
||||
}
|
||||
|
||||
// Save
|
||||
FancyZonesData.WriteLayoutHotkeys(hotkeys);
|
||||
|
||||
// Notify FancyZones
|
||||
notifyFancyZones(wmPrivLayoutHotkeysFileUpdate);
|
||||
|
||||
return (0, $"Hotkey {key} removed");
|
||||
}
|
||||
}
|
||||
@@ -1,276 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace FancyZonesCLI.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Layout-related commands.
|
||||
/// </summary>
|
||||
internal static class LayoutCommands
|
||||
{
|
||||
public static (int ExitCode, string Output) GetLayouts()
|
||||
{
|
||||
var sb = new System.Text.StringBuilder();
|
||||
|
||||
// Print template layouts
|
||||
var templatesJson = FancyZonesData.ReadLayoutTemplates();
|
||||
if (templatesJson?.Templates != null)
|
||||
{
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"=== Built-in Template Layouts ({templatesJson.Templates.Count} total) ===\n");
|
||||
|
||||
for (int i = 0; i < templatesJson.Templates.Count; i++)
|
||||
{
|
||||
var template = templatesJson.Templates[i];
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"[T{i + 1}] {template.Type}");
|
||||
sb.Append(CultureInfo.InvariantCulture, $" Zones: {template.ZoneCount}");
|
||||
if (template.ShowSpacing && template.Spacing > 0)
|
||||
{
|
||||
sb.Append(CultureInfo.InvariantCulture, $", Spacing: {template.Spacing}px");
|
||||
}
|
||||
|
||||
sb.AppendLine();
|
||||
sb.AppendLine();
|
||||
|
||||
// Draw visual preview
|
||||
sb.Append(LayoutVisualizer.DrawTemplateLayout(template));
|
||||
|
||||
if (i < templatesJson.Templates.Count - 1)
|
||||
{
|
||||
sb.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
sb.AppendLine("\n");
|
||||
}
|
||||
|
||||
// Print custom layouts
|
||||
var customLayouts = FancyZonesData.ReadCustomLayouts();
|
||||
if (customLayouts?.Layouts != null)
|
||||
{
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"=== Custom Layouts ({customLayouts.Layouts.Count} total) ===");
|
||||
|
||||
for (int i = 0; i < customLayouts.Layouts.Count; i++)
|
||||
{
|
||||
var layout = customLayouts.Layouts[i];
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"[{i + 1}] {layout.Name}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" UUID: {layout.Uuid}");
|
||||
sb.Append(CultureInfo.InvariantCulture, $" Type: {layout.Type}");
|
||||
|
||||
bool isCanvasLayout = false;
|
||||
if (layout.Info.ValueKind != JsonValueKind.Undefined && layout.Info.ValueKind != JsonValueKind.Null)
|
||||
{
|
||||
if (layout.Type == "grid" && layout.Info.TryGetProperty("rows", out var rows) && layout.Info.TryGetProperty("columns", out var cols))
|
||||
{
|
||||
sb.Append(CultureInfo.InvariantCulture, $" ({rows.GetInt32()}x{cols.GetInt32()} grid)");
|
||||
}
|
||||
else if (layout.Type == "canvas" && layout.Info.TryGetProperty("zones", out var zones))
|
||||
{
|
||||
sb.Append(CultureInfo.InvariantCulture, $" ({zones.GetArrayLength()} zones)");
|
||||
isCanvasLayout = true;
|
||||
}
|
||||
}
|
||||
|
||||
sb.AppendLine("\n");
|
||||
|
||||
// Draw visual preview
|
||||
sb.Append(LayoutVisualizer.DrawCustomLayout(layout));
|
||||
|
||||
// Add note for canvas layouts
|
||||
if (isCanvasLayout)
|
||||
{
|
||||
sb.AppendLine("\n Note: Canvas layout preview is approximate.");
|
||||
sb.AppendLine(" Open FancyZones Editor for precise zone boundaries.");
|
||||
}
|
||||
|
||||
if (i < customLayouts.Layouts.Count - 1)
|
||||
{
|
||||
sb.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
sb.AppendLine("\nUse 'FancyZonesCLI.exe set-layout <UUID>' to apply a layout.");
|
||||
}
|
||||
|
||||
return (0, sb.ToString().TrimEnd());
|
||||
}
|
||||
|
||||
public static (int ExitCode, string Output) GetActiveLayout()
|
||||
{
|
||||
if (!FancyZonesData.TryReadAppliedLayouts(out var appliedLayouts, out var error))
|
||||
{
|
||||
return (1, $"Error: {error}");
|
||||
}
|
||||
|
||||
if (appliedLayouts.Layouts == null || appliedLayouts.Layouts.Count == 0)
|
||||
{
|
||||
return (0, "No active layouts found.");
|
||||
}
|
||||
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.AppendLine("\n=== Active FancyZones Layout(s) ===\n");
|
||||
|
||||
for (int i = 0; i < appliedLayouts.Layouts.Count; i++)
|
||||
{
|
||||
var layout = appliedLayouts.Layouts[i];
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"Monitor {i + 1}:");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" Name: {layout.AppliedLayout.Type}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" UUID: {layout.AppliedLayout.Uuid}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" Type: {layout.AppliedLayout.Type} ({layout.AppliedLayout.ZoneCount} zones)");
|
||||
|
||||
if (layout.AppliedLayout.ShowSpacing)
|
||||
{
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" Spacing: {layout.AppliedLayout.Spacing}px");
|
||||
}
|
||||
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" Sensitivity Radius: {layout.AppliedLayout.SensitivityRadius}px");
|
||||
|
||||
if (i < appliedLayouts.Layouts.Count - 1)
|
||||
{
|
||||
sb.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
return (0, sb.ToString().TrimEnd());
|
||||
}
|
||||
|
||||
public static (int ExitCode, string Output) SetLayout(string[] args, Action<uint> notifyFancyZones, uint wmPrivAppliedLayoutsFileUpdate)
|
||||
{
|
||||
Logger.LogInfo($"SetLayout called with args: [{string.Join(", ", args)}]");
|
||||
|
||||
if (args.Length == 0)
|
||||
{
|
||||
return (1, "Error: set-layout requires a UUID parameter");
|
||||
}
|
||||
|
||||
string uuid = args[0];
|
||||
int? targetMonitor = null;
|
||||
bool applyToAll = false;
|
||||
|
||||
// Parse options
|
||||
for (int i = 1; i < args.Length; i++)
|
||||
{
|
||||
if (args[i] == "--monitor" && i + 1 < args.Length)
|
||||
{
|
||||
if (int.TryParse(args[i + 1], out int monitorNum))
|
||||
{
|
||||
targetMonitor = monitorNum;
|
||||
i++; // Skip next arg
|
||||
}
|
||||
else
|
||||
{
|
||||
return (1, $"Error: Invalid monitor number: {args[i + 1]}");
|
||||
}
|
||||
}
|
||||
else if (args[i] == "--all")
|
||||
{
|
||||
applyToAll = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (targetMonitor.HasValue && applyToAll)
|
||||
{
|
||||
return (1, "Error: Cannot specify both --monitor and --all");
|
||||
}
|
||||
|
||||
// Try to find layout in custom layouts first (by UUID)
|
||||
var customLayouts = FancyZonesData.ReadCustomLayouts();
|
||||
var targetCustomLayout = customLayouts?.Layouts?.FirstOrDefault(l => l.Uuid.Equals(uuid, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
// If not found in custom layouts, try template layouts (by type name)
|
||||
TemplateLayout targetTemplate = null;
|
||||
if (targetCustomLayout == null)
|
||||
{
|
||||
var templates = FancyZonesData.ReadLayoutTemplates();
|
||||
targetTemplate = templates?.Templates?.FirstOrDefault(t => t.Type.Equals(uuid, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
if (targetCustomLayout == null && targetTemplate == null)
|
||||
{
|
||||
return (1, $"Error: Layout '{uuid}' not found\nTip: For templates, use the type name (e.g., 'focus', 'columns', 'rows', 'grid', 'priority-grid')\n For custom layouts, use the UUID from 'get-layouts'");
|
||||
}
|
||||
|
||||
// Read current applied layouts
|
||||
if (!FancyZonesData.TryReadAppliedLayouts(out var appliedLayouts, out var error))
|
||||
{
|
||||
return (1, $"Error: {error}");
|
||||
}
|
||||
|
||||
if (appliedLayouts.Layouts == null || appliedLayouts.Layouts.Count == 0)
|
||||
{
|
||||
return (1, "Error: No monitors configured");
|
||||
}
|
||||
|
||||
// Determine which monitors to update
|
||||
List<int> monitorsToUpdate = new List<int>();
|
||||
if (applyToAll)
|
||||
{
|
||||
for (int i = 0; i < appliedLayouts.Layouts.Count; i++)
|
||||
{
|
||||
monitorsToUpdate.Add(i);
|
||||
}
|
||||
}
|
||||
else if (targetMonitor.HasValue)
|
||||
{
|
||||
int monitorIndex = targetMonitor.Value - 1; // Convert to 0-based
|
||||
if (monitorIndex < 0 || monitorIndex >= appliedLayouts.Layouts.Count)
|
||||
{
|
||||
return (1, $"Error: Monitor {targetMonitor.Value} not found. Available monitors: 1-{appliedLayouts.Layouts.Count}");
|
||||
}
|
||||
|
||||
monitorsToUpdate.Add(monitorIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Default: first monitor
|
||||
monitorsToUpdate.Add(0);
|
||||
}
|
||||
|
||||
// Update selected monitors
|
||||
foreach (int monitorIndex in monitorsToUpdate)
|
||||
{
|
||||
if (targetCustomLayout != null)
|
||||
{
|
||||
appliedLayouts.Layouts[monitorIndex].AppliedLayout.Uuid = targetCustomLayout.Uuid;
|
||||
appliedLayouts.Layouts[monitorIndex].AppliedLayout.Type = targetCustomLayout.Type;
|
||||
}
|
||||
else if (targetTemplate != null)
|
||||
{
|
||||
// For templates, use all-zeros UUID and the template type
|
||||
appliedLayouts.Layouts[monitorIndex].AppliedLayout.Uuid = "{00000000-0000-0000-0000-000000000000}";
|
||||
appliedLayouts.Layouts[monitorIndex].AppliedLayout.Type = targetTemplate.Type;
|
||||
appliedLayouts.Layouts[monitorIndex].AppliedLayout.ZoneCount = targetTemplate.ZoneCount;
|
||||
appliedLayouts.Layouts[monitorIndex].AppliedLayout.ShowSpacing = targetTemplate.ShowSpacing;
|
||||
appliedLayouts.Layouts[monitorIndex].AppliedLayout.Spacing = targetTemplate.Spacing;
|
||||
}
|
||||
}
|
||||
|
||||
// Write back to file
|
||||
FancyZonesData.WriteAppliedLayouts(appliedLayouts);
|
||||
Logger.LogInfo($"Applied layouts file updated for {monitorsToUpdate.Count} monitor(s)");
|
||||
|
||||
// Notify FancyZones to reload
|
||||
notifyFancyZones(wmPrivAppliedLayoutsFileUpdate);
|
||||
Logger.LogInfo("FancyZones notified of layout change");
|
||||
|
||||
string layoutName = targetCustomLayout?.Name ?? targetTemplate?.Type ?? uuid;
|
||||
if (applyToAll)
|
||||
{
|
||||
return (0, $"Layout '{layoutName}' applied to all {monitorsToUpdate.Count} monitors");
|
||||
}
|
||||
else if (targetMonitor.HasValue)
|
||||
{
|
||||
return (0, $"Layout '{layoutName}' applied to monitor {targetMonitor.Value}");
|
||||
}
|
||||
else
|
||||
{
|
||||
return (0, $"Layout '{layoutName}' applied to monitor 1");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Globalization;
|
||||
|
||||
namespace FancyZonesCLI.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Monitor-related commands.
|
||||
/// </summary>
|
||||
internal static class MonitorCommands
|
||||
{
|
||||
public static (int ExitCode, string Output) GetMonitors()
|
||||
{
|
||||
if (!FancyZonesData.TryReadAppliedLayouts(out var appliedLayouts, out var error))
|
||||
{
|
||||
return (1, $"Error: {error}");
|
||||
}
|
||||
|
||||
if (appliedLayouts.Layouts == null || appliedLayouts.Layouts.Count == 0)
|
||||
{
|
||||
return (0, "No monitors found.");
|
||||
}
|
||||
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"=== Monitors ({appliedLayouts.Layouts.Count} total) ===");
|
||||
sb.AppendLine();
|
||||
|
||||
for (int i = 0; i < appliedLayouts.Layouts.Count; i++)
|
||||
{
|
||||
var layout = appliedLayouts.Layouts[i];
|
||||
var monitorNum = i + 1;
|
||||
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"Monitor {monitorNum}:");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" Monitor: {layout.Device.Monitor}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" Monitor Instance: {layout.Device.MonitorInstance}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" Monitor Number: {layout.Device.MonitorNumber}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" Serial Number: {layout.Device.SerialNumber}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" Virtual Desktop: {layout.Device.VirtualDesktop}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" Sensitivity Radius: {layout.AppliedLayout.SensitivityRadius}px");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" Active Layout: {layout.AppliedLayout.Type}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $" Zone Count: {layout.AppliedLayout.ZoneCount}");
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
return (0, sb.ToString().TrimEnd());
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
<!-- Look at Directory.Build.props in root for common stuff as well -->
|
||||
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\..\Common.SelfContained.props" />
|
||||
<Import Project="..\..\..\Common.Dotnet.AotCompatibility.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<AssemblyTitle>PowerToys.FancyZonesCLI</AssemblyTitle>
|
||||
@@ -10,8 +9,6 @@
|
||||
<Description>PowerToys FancyZones CLI</Description>
|
||||
<OutputType>Exe</OutputType>
|
||||
<Platforms>x64;ARM64</Platforms>
|
||||
<PublishAot>true</PublishAot>
|
||||
<DisableRuntimeMarshalling>true</DisableRuntimeMarshalling>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)</OutputPath>
|
||||
@@ -21,9 +18,14 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Text.Json" />
|
||||
<PackageReference Include="System.CommandLine" />
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\FancyZonesEditorCommon\FancyZonesEditorCommon.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Force using WindowsDesktop runtime to ensure consistent dll versions with other projects -->
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.WindowsDesktop.App" />
|
||||
|
||||
@@ -1,142 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization.Metadata;
|
||||
|
||||
namespace FancyZonesCLI;
|
||||
|
||||
/// <summary>
|
||||
/// Provides methods to read and write FancyZones configuration data.
|
||||
/// </summary>
|
||||
internal static class FancyZonesData
|
||||
{
|
||||
/// <summary>
|
||||
/// Try to read applied layouts configuration.
|
||||
/// </summary>
|
||||
public static bool TryReadAppliedLayouts(out AppliedLayouts result, out string error)
|
||||
{
|
||||
return TryReadJsonFile(FancyZonesPaths.AppliedLayouts, FancyZonesJsonContext.Default.AppliedLayouts, out result, out error);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read applied layouts or return null if not found.
|
||||
/// </summary>
|
||||
public static AppliedLayouts ReadAppliedLayouts()
|
||||
{
|
||||
return ReadJsonFileOrDefault(FancyZonesPaths.AppliedLayouts, FancyZonesJsonContext.Default.AppliedLayouts);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write applied layouts configuration.
|
||||
/// </summary>
|
||||
public static void WriteAppliedLayouts(AppliedLayouts layouts)
|
||||
{
|
||||
WriteJsonFile(FancyZonesPaths.AppliedLayouts, layouts, FancyZonesJsonContext.Default.AppliedLayouts);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read custom layouts or return null if not found.
|
||||
/// </summary>
|
||||
public static CustomLayouts ReadCustomLayouts()
|
||||
{
|
||||
return ReadJsonFileOrDefault(FancyZonesPaths.CustomLayouts, FancyZonesJsonContext.Default.CustomLayouts);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read layout templates or return null if not found.
|
||||
/// </summary>
|
||||
public static LayoutTemplates ReadLayoutTemplates()
|
||||
{
|
||||
return ReadJsonFileOrDefault(FancyZonesPaths.LayoutTemplates, FancyZonesJsonContext.Default.LayoutTemplates);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read layout hotkeys or return null if not found.
|
||||
/// </summary>
|
||||
public static LayoutHotkeys ReadLayoutHotkeys()
|
||||
{
|
||||
return ReadJsonFileOrDefault(FancyZonesPaths.LayoutHotkeys, FancyZonesJsonContext.Default.LayoutHotkeys);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write layout hotkeys configuration.
|
||||
/// </summary>
|
||||
public static void WriteLayoutHotkeys(LayoutHotkeys hotkeys)
|
||||
{
|
||||
WriteJsonFile(FancyZonesPaths.LayoutHotkeys, hotkeys, FancyZonesJsonContext.Default.LayoutHotkeys);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if editor parameters file exists.
|
||||
/// </summary>
|
||||
public static bool EditorParametersExist()
|
||||
{
|
||||
return File.Exists(FancyZonesPaths.EditorParameters);
|
||||
}
|
||||
|
||||
private static bool TryReadJsonFile<T>(string filePath, JsonTypeInfo<T> jsonTypeInfo, out T result, out string error)
|
||||
where T : class
|
||||
{
|
||||
result = null;
|
||||
error = null;
|
||||
|
||||
Logger.LogDebug($"Reading file: {filePath}");
|
||||
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
error = $"File not found: {Path.GetFileName(filePath)}";
|
||||
Logger.LogWarning(error);
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var json = File.ReadAllText(filePath);
|
||||
result = JsonSerializer.Deserialize(json, jsonTypeInfo);
|
||||
if (result == null)
|
||||
{
|
||||
error = $"Failed to parse {Path.GetFileName(filePath)}";
|
||||
Logger.LogError(error);
|
||||
return false;
|
||||
}
|
||||
|
||||
Logger.LogDebug($"Successfully read {Path.GetFileName(filePath)}");
|
||||
return true;
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
error = $"JSON parse error in {Path.GetFileName(filePath)}: {ex.Message}";
|
||||
Logger.LogError(error, ex);
|
||||
return false;
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
error = $"Failed to read {Path.GetFileName(filePath)}: {ex.Message}";
|
||||
Logger.LogError(error, ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static T ReadJsonFileOrDefault<T>(string filePath, JsonTypeInfo<T> jsonTypeInfo, T defaultValue = null)
|
||||
where T : class
|
||||
{
|
||||
if (TryReadJsonFile(filePath, jsonTypeInfo, out var result, out _))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
private static void WriteJsonFile<T>(string filePath, T data, JsonTypeInfo<T> jsonTypeInfo)
|
||||
{
|
||||
Logger.LogDebug($"Writing file: {filePath}");
|
||||
var json = JsonSerializer.Serialize(data, jsonTypeInfo);
|
||||
File.WriteAllText(filePath, json);
|
||||
Logger.LogInfo($"Successfully wrote {Path.GetFileName(filePath)}");
|
||||
}
|
||||
}
|
||||
@@ -7,12 +7,13 @@ using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using FancyZonesEditorCommon.Data;
|
||||
|
||||
namespace FancyZonesCLI;
|
||||
|
||||
public static class LayoutVisualizer
|
||||
{
|
||||
public static string DrawTemplateLayout(TemplateLayout template)
|
||||
public static string DrawTemplateLayout(LayoutTemplates.TemplateLayoutWrapper template)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine(" Visual Preview:");
|
||||
@@ -62,7 +63,7 @@ public static class LayoutVisualizer
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static string DrawCustomLayout(CustomLayout layout)
|
||||
public static string DrawCustomLayout(CustomLayouts.CustomLayoutWrapper layout)
|
||||
{
|
||||
if (layout.Info.ValueKind == JsonValueKind.Undefined || layout.Info.ValueKind == JsonValueKind.Null)
|
||||
{
|
||||
@@ -426,6 +427,11 @@ public static class LayoutVisualizer
|
||||
const int displayWidth = 49;
|
||||
const int displayHeight = 15;
|
||||
|
||||
if (refWidth <= 0 || refHeight <= 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
// Create a 2D array to track which zones occupy each position
|
||||
var zoneGrid = new List<int>[displayHeight, displayWidth];
|
||||
for (int i = 0; i < displayHeight; i++)
|
||||
@@ -442,10 +448,13 @@ public static class LayoutVisualizer
|
||||
|
||||
foreach (var zone in zones.EnumerateArray())
|
||||
{
|
||||
int x = zone.GetProperty("X").GetInt32();
|
||||
int y = zone.GetProperty("Y").GetInt32();
|
||||
int w = zone.GetProperty("width").GetInt32();
|
||||
int h = zone.GetProperty("height").GetInt32();
|
||||
if (!TryGetInt32Property(zone, "x", "X", out int x) ||
|
||||
!TryGetInt32Property(zone, "y", "Y", out int y) ||
|
||||
!TryGetInt32Property(zone, "width", "Width", out int w) ||
|
||||
!TryGetInt32Property(zone, "height", "Height", out int h))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int dx = Math.Max(0, Math.Min(displayWidth - 1, x * displayWidth / refWidth));
|
||||
int dy = Math.Max(0, Math.Min(displayHeight - 1, y * displayHeight / refHeight));
|
||||
@@ -547,4 +556,23 @@ public static class LayoutVisualizer
|
||||
sb.AppendLine();
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static bool TryGetInt32Property(JsonElement element, string primaryName, string fallbackName, out int value)
|
||||
{
|
||||
if (element.TryGetProperty(primaryName, out var property) || element.TryGetProperty(fallbackName, out property))
|
||||
{
|
||||
if (property.ValueKind == JsonValueKind.Number)
|
||||
{
|
||||
return property.TryGetInt32(out value);
|
||||
}
|
||||
|
||||
if (property.ValueKind == JsonValueKind.String)
|
||||
{
|
||||
return int.TryParse(property.GetString(), NumberStyles.Integer, CultureInfo.InvariantCulture, out value);
|
||||
}
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace FancyZonesCLI;
|
||||
|
||||
// JSON Source Generator for AOT compatibility
|
||||
[JsonSerializable(typeof(LayoutTemplates))]
|
||||
[JsonSerializable(typeof(CustomLayouts))]
|
||||
[JsonSerializable(typeof(AppliedLayouts))]
|
||||
[JsonSerializable(typeof(LayoutHotkeys))]
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
internal partial class FancyZonesJsonContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
|
||||
// Layout Templates
|
||||
public sealed class LayoutTemplates
|
||||
{
|
||||
[JsonPropertyName("layout-templates")]
|
||||
public List<TemplateLayout> Templates { get; set; }
|
||||
}
|
||||
|
||||
public sealed class TemplateLayout
|
||||
{
|
||||
[JsonPropertyName("type")]
|
||||
public string Type { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("zone-count")]
|
||||
public int ZoneCount { get; set; }
|
||||
|
||||
[JsonPropertyName("show-spacing")]
|
||||
public bool ShowSpacing { get; set; }
|
||||
|
||||
[JsonPropertyName("spacing")]
|
||||
public int Spacing { get; set; }
|
||||
|
||||
[JsonPropertyName("sensitivity-radius")]
|
||||
public int SensitivityRadius { get; set; }
|
||||
}
|
||||
|
||||
// Custom Layouts
|
||||
public sealed class CustomLayouts
|
||||
{
|
||||
[JsonPropertyName("custom-layouts")]
|
||||
public List<CustomLayout> Layouts { get; set; }
|
||||
}
|
||||
|
||||
public sealed class CustomLayout
|
||||
{
|
||||
[JsonPropertyName("uuid")]
|
||||
public string Uuid { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("type")]
|
||||
public string Type { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("info")]
|
||||
public JsonElement Info { get; set; }
|
||||
}
|
||||
|
||||
// Applied Layouts
|
||||
public sealed class AppliedLayouts
|
||||
{
|
||||
[JsonPropertyName("applied-layouts")]
|
||||
public List<AppliedLayoutWrapper> Layouts { get; set; }
|
||||
}
|
||||
|
||||
public sealed class AppliedLayoutWrapper
|
||||
{
|
||||
[JsonPropertyName("device")]
|
||||
public DeviceInfo Device { get; set; } = new();
|
||||
|
||||
[JsonPropertyName("applied-layout")]
|
||||
public AppliedLayoutInfo AppliedLayout { get; set; } = new();
|
||||
}
|
||||
|
||||
public sealed class DeviceInfo
|
||||
{
|
||||
[JsonPropertyName("monitor")]
|
||||
public string Monitor { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("monitor-instance")]
|
||||
public string MonitorInstance { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("monitor-number")]
|
||||
public int MonitorNumber { get; set; }
|
||||
|
||||
[JsonPropertyName("serial-number")]
|
||||
public string SerialNumber { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("virtual-desktop")]
|
||||
public string VirtualDesktop { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public sealed class AppliedLayoutInfo
|
||||
{
|
||||
[JsonPropertyName("uuid")]
|
||||
public string Uuid { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("type")]
|
||||
public string Type { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("show-spacing")]
|
||||
public bool ShowSpacing { get; set; }
|
||||
|
||||
[JsonPropertyName("spacing")]
|
||||
public int Spacing { get; set; }
|
||||
|
||||
[JsonPropertyName("zone-count")]
|
||||
public int ZoneCount { get; set; }
|
||||
|
||||
[JsonPropertyName("sensitivity-radius")]
|
||||
public int SensitivityRadius { get; set; }
|
||||
}
|
||||
|
||||
// Layout Hotkeys
|
||||
public sealed class LayoutHotkeys
|
||||
{
|
||||
[JsonPropertyName("layout-hotkeys")]
|
||||
public List<LayoutHotkey> Hotkeys { get; set; }
|
||||
}
|
||||
|
||||
public sealed class LayoutHotkey
|
||||
{
|
||||
[JsonPropertyName("key")]
|
||||
public int Key { get; set; }
|
||||
|
||||
[JsonPropertyName("layout-id")]
|
||||
public string LayoutId { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -15,6 +15,7 @@ internal static class NativeMethods
|
||||
// Registered Windows messages for notifying FancyZones
|
||||
private static uint wmPrivAppliedLayoutsFileUpdate;
|
||||
private static uint wmPrivLayoutHotkeysFileUpdate;
|
||||
private static uint wmPrivSaveEditorParameters;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Windows message ID for applied layouts file update notification.
|
||||
@@ -26,6 +27,11 @@ internal static class NativeMethods
|
||||
/// </summary>
|
||||
public static uint WM_PRIV_LAYOUT_HOTKEYS_FILE_UPDATE => wmPrivLayoutHotkeysFileUpdate;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Windows message ID used to request saving editor-parameters.json.
|
||||
/// </summary>
|
||||
public static uint WM_PRIV_SAVE_EDITOR_PARAMETERS => wmPrivSaveEditorParameters;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the Windows messages used for FancyZones notifications.
|
||||
/// </summary>
|
||||
@@ -33,6 +39,7 @@ internal static class NativeMethods
|
||||
{
|
||||
wmPrivAppliedLayoutsFileUpdate = PInvoke.RegisterWindowMessage("{2ef2c8a7-e0d5-4f31-9ede-52aade2d284d}");
|
||||
wmPrivLayoutHotkeysFileUpdate = PInvoke.RegisterWindowMessage("{07229b7e-4f22-4357-b136-33c289be2295}");
|
||||
wmPrivSaveEditorParameters = PInvoke.RegisterWindowMessage("{d8f9c0e3-5d77-4e83-8a4f-7c704c2bfb4a}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -3,113 +3,44 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.CommandLine;
|
||||
using System.Linq;
|
||||
using FancyZonesCLI.Commands;
|
||||
using System.Threading.Tasks;
|
||||
using FancyZonesCLI.CommandLine;
|
||||
|
||||
namespace FancyZonesCLI;
|
||||
|
||||
internal sealed class Program
|
||||
{
|
||||
private static int Main(string[] args)
|
||||
private static async Task<int> Main(string[] args)
|
||||
{
|
||||
// Initialize logger
|
||||
Logger.InitializeLogger();
|
||||
Logger.LogInfo($"CLI invoked with args: [{string.Join(", ", args)}]");
|
||||
|
||||
// Initialize Windows messages
|
||||
// Initialize Windows messages used to notify FancyZones.
|
||||
NativeMethods.InitializeWindowMessages();
|
||||
|
||||
(int ExitCode, string Output) result;
|
||||
|
||||
if (args.Length == 0)
|
||||
// Intercept help requests early and print custom usage.
|
||||
if (args.Any(a => string.Equals(a, "--help", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(a, "-h", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(a, "-?", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
result = (1, GetUsageText());
|
||||
FancyZonesCliUsage.PrintUsage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
RootCommand rootCommand = FancyZonesCliCommandFactory.CreateRootCommand();
|
||||
int exitCode = await rootCommand.InvokeAsync(args);
|
||||
|
||||
if (exitCode == 0)
|
||||
{
|
||||
Logger.LogInfo("Command completed successfully");
|
||||
}
|
||||
else
|
||||
{
|
||||
var command = args[0].ToLowerInvariant();
|
||||
|
||||
result = command switch
|
||||
{
|
||||
"open-editor" or "editor" or "e" => EditorCommands.OpenEditor(),
|
||||
"get-monitors" or "monitors" or "m" => MonitorCommands.GetMonitors(),
|
||||
"get-layouts" or "layouts" or "ls" => LayoutCommands.GetLayouts(),
|
||||
"get-active-layout" or "active" or "get-active" or "a" => LayoutCommands.GetActiveLayout(),
|
||||
"set-layout" or "set" or "s" => args.Length >= 2
|
||||
? LayoutCommands.SetLayout(args.Skip(1).ToArray(), NativeMethods.NotifyFancyZones, NativeMethods.WM_PRIV_APPLIED_LAYOUTS_FILE_UPDATE)
|
||||
: (1, "Error: set-layout requires a UUID parameter"),
|
||||
"open-settings" or "settings" => EditorCommands.OpenSettings(),
|
||||
"get-hotkeys" or "hotkeys" or "hk" => HotkeyCommands.GetHotkeys(),
|
||||
"set-hotkey" or "shk" => args.Length >= 3
|
||||
? HotkeyCommands.SetHotkey(int.Parse(args[1], CultureInfo.InvariantCulture), args[2], NativeMethods.NotifyFancyZones, NativeMethods.WM_PRIV_LAYOUT_HOTKEYS_FILE_UPDATE)
|
||||
: (1, "Error: set-hotkey requires <key> <uuid>"),
|
||||
"remove-hotkey" or "rhk" => args.Length >= 2
|
||||
? HotkeyCommands.RemoveHotkey(int.Parse(args[1], CultureInfo.InvariantCulture), NativeMethods.NotifyFancyZones, NativeMethods.WM_PRIV_LAYOUT_HOTKEYS_FILE_UPDATE)
|
||||
: (1, "Error: remove-hotkey requires <key>"),
|
||||
"help" or "--help" or "-h" => (0, GetUsageText()),
|
||||
_ => (1, $"Error: Unknown command: {command}\n\n{GetUsageText()}"),
|
||||
};
|
||||
Logger.LogWarning($"Command failed with exit code {exitCode}");
|
||||
}
|
||||
|
||||
// Log result
|
||||
if (result.ExitCode == 0)
|
||||
{
|
||||
Logger.LogInfo($"Command completed successfully");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogWarning($"Command failed with exit code {result.ExitCode}: {result.Output}");
|
||||
}
|
||||
|
||||
// Output result
|
||||
if (!string.IsNullOrEmpty(result.Output))
|
||||
{
|
||||
Console.WriteLine(result.Output);
|
||||
}
|
||||
|
||||
return result.ExitCode;
|
||||
}
|
||||
|
||||
private static string GetUsageText()
|
||||
{
|
||||
return """
|
||||
FancyZones CLI - Command line interface for FancyZones
|
||||
======================================================
|
||||
|
||||
Usage: FancyZonesCLI.exe <command> [options]
|
||||
|
||||
Commands:
|
||||
open-editor (editor, e) Launch FancyZones layout editor
|
||||
get-monitors (monitors, m) List all monitors and their properties
|
||||
get-layouts (layouts, ls) List all available layouts
|
||||
get-active-layout (get-active, active, a)
|
||||
Show currently active layout
|
||||
set-layout (set, s) <uuid> [options]
|
||||
Set layout by UUID
|
||||
--monitor <n> Apply to monitor N (1-based)
|
||||
--all Apply to all monitors
|
||||
open-settings (settings) Open FancyZones settings page
|
||||
get-hotkeys (hotkeys, hk) List all layout hotkeys
|
||||
set-hotkey (shk) <key> <uuid> Assign hotkey (0-9) to CUSTOM layout
|
||||
Note: Only custom layouts work with hotkeys
|
||||
remove-hotkey (rhk) <key> Remove hotkey assignment
|
||||
help Show this help message
|
||||
|
||||
|
||||
Examples:
|
||||
FancyZonesCLI.exe e # Open editor (short)
|
||||
FancyZonesCLI.exe m # List monitors (short)
|
||||
FancyZonesCLI.exe ls # List layouts (short)
|
||||
FancyZonesCLI.exe a # Get active layout (short)
|
||||
FancyZonesCLI.exe s focus --all # Set layout (short)
|
||||
FancyZonesCLI.exe open-editor # Open editor (long)
|
||||
FancyZonesCLI.exe get-monitors
|
||||
FancyZonesCLI.exe get-layouts
|
||||
FancyZonesCLI.exe set-layout {12345678-1234-1234-1234-123456789012}
|
||||
FancyZonesCLI.exe set-layout focus --monitor 2
|
||||
FancyZonesCLI.exe set-layout columns --all
|
||||
FancyZonesCLI.exe set-hotkey 3 {12345678-1234-1234-1234-123456789012}
|
||||
""";
|
||||
return exitCode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using FancyZonesEditorCommon.Data;
|
||||
|
||||
namespace FancyZonesCLI.Utils;
|
||||
|
||||
/// <summary>
|
||||
/// Helper for managing applied layouts across monitors.
|
||||
/// CLI-only business logic for matching, finding, and updating applied layouts.
|
||||
/// </summary>
|
||||
internal static class AppliedLayoutsHelper
|
||||
{
|
||||
public const string DefaultVirtualDesktopGuid = "{00000000-0000-0000-0000-000000000000}";
|
||||
|
||||
public static bool MatchesDevice(
|
||||
AppliedLayouts.AppliedLayoutWrapper.DeviceIdWrapper device,
|
||||
string monitorName,
|
||||
string serialNumber,
|
||||
int monitorNumber,
|
||||
string virtualDesktop)
|
||||
{
|
||||
// Must match monitor name
|
||||
if (device.Monitor != monitorName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Must match virtual desktop
|
||||
if (device.VirtualDesktop != virtualDesktop)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If serial numbers are both available, they must match
|
||||
if (!string.IsNullOrEmpty(device.SerialNumber) && !string.IsNullOrEmpty(serialNumber))
|
||||
{
|
||||
if (device.SerialNumber != serialNumber)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If we reach here: Monitor name, VirtualDesktop, and SerialNumber (if available) all match
|
||||
// MonitorInstance and MonitorNumber can vary, so we accept any value
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool MatchesDeviceWithDefaultVirtualDesktop(
|
||||
AppliedLayouts.AppliedLayoutWrapper.DeviceIdWrapper device,
|
||||
string monitorName,
|
||||
string serialNumber,
|
||||
int monitorNumber,
|
||||
string virtualDesktop)
|
||||
{
|
||||
if (device.VirtualDesktop == DefaultVirtualDesktopGuid)
|
||||
{
|
||||
// For this one layout record only, match any virtual desktop.
|
||||
return device.Monitor == monitorName;
|
||||
}
|
||||
|
||||
return MatchesDevice(device, monitorName, serialNumber, monitorNumber, virtualDesktop);
|
||||
}
|
||||
|
||||
public static AppliedLayouts.AppliedLayoutWrapper? FindLayoutForMonitor(
|
||||
AppliedLayouts.AppliedLayoutsListWrapper layouts,
|
||||
string monitorName,
|
||||
string serialNumber,
|
||||
int monitorNumber,
|
||||
string virtualDesktop)
|
||||
{
|
||||
if (layouts.AppliedLayouts == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (var layout in layouts.AppliedLayouts)
|
||||
{
|
||||
if (MatchesDevice(layout.Device, monitorName, serialNumber, monitorNumber, virtualDesktop))
|
||||
{
|
||||
return layout;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
|
||||
using FancyZonesEditorCommon.Data;
|
||||
using FancyZonesEditorCommon.Utils;
|
||||
|
||||
namespace FancyZonesCLI.Utils;
|
||||
|
||||
/// <summary>
|
||||
/// Helper for requesting FancyZones to save editor-parameters.json and reading it reliably.
|
||||
/// </summary>
|
||||
internal static class EditorParametersRefresh
|
||||
{
|
||||
public static EditorParameters.ParamsWrapper ReadEditorParametersWithRefresh(Action requestSave)
|
||||
{
|
||||
const int maxWaitMilliseconds = 500;
|
||||
const int pollIntervalMilliseconds = 50;
|
||||
|
||||
string filePath = FancyZonesPaths.EditorParameters;
|
||||
DateTime lastWriteBefore = File.Exists(filePath) ? File.GetLastWriteTimeUtc(filePath) : DateTime.MinValue;
|
||||
|
||||
requestSave();
|
||||
|
||||
int elapsedMilliseconds = 0;
|
||||
while (elapsedMilliseconds < maxWaitMilliseconds)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
DateTime lastWriteNow = File.GetLastWriteTimeUtc(filePath);
|
||||
|
||||
// Prefer reading after the file is updated, but don't block forever if the
|
||||
// timestamp resolution is coarse or FancyZones rewrites identical content.
|
||||
if (lastWriteNow >= lastWriteBefore || elapsedMilliseconds > 100)
|
||||
{
|
||||
var editorParams = FancyZonesDataIO.ReadEditorParameters();
|
||||
if (editorParams.Monitors != null && editorParams.Monitors.Count > 0)
|
||||
{
|
||||
return editorParams;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (ex is FileNotFoundException || ex is IOException || ex is UnauthorizedAccessException || ex is JsonException)
|
||||
{
|
||||
// File may be mid-write/locked or temporarily invalid JSON; retry.
|
||||
}
|
||||
|
||||
Thread.Sleep(pollIntervalMilliseconds);
|
||||
elapsedMilliseconds += pollIntervalMilliseconds;
|
||||
}
|
||||
|
||||
var finalParams = FancyZonesDataIO.ReadEditorParameters();
|
||||
if (finalParams.Monitors == null || finalParams.Monitors.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException($"Could not get current monitor information (timed out after {maxWaitMilliseconds}ms waiting for '{Path.GetFileName(filePath)}').");
|
||||
}
|
||||
|
||||
return finalParams;
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ namespace FancyZonesEditorCommon.Data
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetDataFolder() + "\\Microsoft\\PowerToys\\FancyZones\\applied-layouts.json";
|
||||
return FancyZonesPaths.AppliedLayouts;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace FancyZonesEditorCommon.Data
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetDataFolder() + "\\Microsoft\\PowerToys\\FancyZones\\custom-layouts.json";
|
||||
return FancyZonesPaths.CustomLayouts;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace FancyZonesEditorCommon.Data
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetDataFolder() + "\\Microsoft\\PowerToys\\FancyZones\\default-layouts.json";
|
||||
return FancyZonesPaths.DefaultLayouts;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace FancyZonesEditorCommon.Data
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetDataFolder() + "\\Microsoft\\PowerToys\\FancyZones\\editor-parameters.json";
|
||||
return FancyZonesPaths.EditorParameters;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace FancyZonesCLI;
|
||||
namespace FancyZonesEditorCommon.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Provides paths to FancyZones configuration files.
|
||||
/// </summary>
|
||||
internal static class FancyZonesPaths
|
||||
public static class FancyZonesPaths
|
||||
{
|
||||
private static readonly string DataPath = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||
@@ -27,4 +27,6 @@ internal static class FancyZonesPaths
|
||||
public static string LayoutHotkeys => Path.Combine(DataPath, "layout-hotkeys.json");
|
||||
|
||||
public static string EditorParameters => Path.Combine(DataPath, "editor-parameters.json");
|
||||
|
||||
public static string DefaultLayouts => Path.Combine(DataPath, "default-layouts.json");
|
||||
}
|
||||
@@ -14,7 +14,7 @@ namespace FancyZonesEditorCommon.Data
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetDataFolder() + "\\Microsoft\\PowerToys\\FancyZones\\layout-hotkeys.json";
|
||||
return FancyZonesPaths.LayoutHotkeys;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace FancyZonesEditorCommon.Data
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetDataFolder() + "\\Microsoft\\PowerToys\\FancyZones\\layout-templates.json";
|
||||
return FancyZonesPaths.LayoutTemplates;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using FancyZonesEditorCommon.Data;
|
||||
|
||||
namespace FancyZonesEditorCommon.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Unified helper for all FancyZones data file I/O operations.
|
||||
/// Centralizes reading and writing of all JSON configuration files.
|
||||
/// </summary>
|
||||
public static class FancyZonesDataIO
|
||||
{
|
||||
private static TWrapper ReadData<TData, TWrapper>(
|
||||
Func<TData> createInstance,
|
||||
Func<TData, string> fileSelector,
|
||||
Func<TData, string, TWrapper> readFunc)
|
||||
{
|
||||
var instance = createInstance();
|
||||
string filePath = fileSelector(instance);
|
||||
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
throw new FileNotFoundException($"File not found: {Path.GetFileName(filePath)}", filePath);
|
||||
}
|
||||
|
||||
return readFunc(instance, filePath);
|
||||
}
|
||||
|
||||
private static void WriteData<TData, TWrapper>(
|
||||
Func<TData> createInstance,
|
||||
Func<TData, string> fileSelector,
|
||||
Func<TData, TWrapper, string> serializeFunc,
|
||||
TWrapper data)
|
||||
{
|
||||
var instance = createInstance();
|
||||
var filePath = fileSelector(instance);
|
||||
|
||||
IOUtils ioUtils = new IOUtils();
|
||||
ioUtils.WriteFile(filePath, serializeFunc(instance, data));
|
||||
}
|
||||
|
||||
// AppliedLayouts operations
|
||||
public static AppliedLayouts.AppliedLayoutsListWrapper ReadAppliedLayouts()
|
||||
{
|
||||
return ReadData(
|
||||
() => new AppliedLayouts(),
|
||||
instance => instance.File,
|
||||
(instance, file) => instance.Read(file));
|
||||
}
|
||||
|
||||
public static void WriteAppliedLayouts(AppliedLayouts.AppliedLayoutsListWrapper data)
|
||||
{
|
||||
WriteData(
|
||||
() => new AppliedLayouts(),
|
||||
instance => instance.File,
|
||||
(instance, wrapper) => instance.Serialize(wrapper),
|
||||
data);
|
||||
}
|
||||
|
||||
// CustomLayouts operations
|
||||
public static CustomLayouts.CustomLayoutListWrapper ReadCustomLayouts()
|
||||
{
|
||||
return ReadData(
|
||||
() => new CustomLayouts(),
|
||||
instance => instance.File,
|
||||
(instance, file) => instance.Read(file));
|
||||
}
|
||||
|
||||
public static void WriteCustomLayouts(CustomLayouts.CustomLayoutListWrapper data)
|
||||
{
|
||||
WriteData(
|
||||
() => new CustomLayouts(),
|
||||
instance => instance.File,
|
||||
(instance, wrapper) => instance.Serialize(wrapper),
|
||||
data);
|
||||
}
|
||||
|
||||
// LayoutTemplates operations
|
||||
public static LayoutTemplates.TemplateLayoutsListWrapper ReadLayoutTemplates()
|
||||
{
|
||||
return ReadData(
|
||||
() => new LayoutTemplates(),
|
||||
instance => instance.File,
|
||||
(instance, file) => instance.Read(file));
|
||||
}
|
||||
|
||||
public static void WriteLayoutTemplates(LayoutTemplates.TemplateLayoutsListWrapper data)
|
||||
{
|
||||
WriteData(
|
||||
() => new LayoutTemplates(),
|
||||
instance => instance.File,
|
||||
(instance, wrapper) => instance.Serialize(wrapper),
|
||||
data);
|
||||
}
|
||||
|
||||
// LayoutHotkeys operations
|
||||
public static LayoutHotkeys.LayoutHotkeysWrapper ReadLayoutHotkeys()
|
||||
{
|
||||
return ReadData(
|
||||
() => new LayoutHotkeys(),
|
||||
instance => instance.File,
|
||||
(instance, file) => instance.Read(file));
|
||||
}
|
||||
|
||||
public static void WriteLayoutHotkeys(LayoutHotkeys.LayoutHotkeysWrapper data)
|
||||
{
|
||||
WriteData(
|
||||
() => new LayoutHotkeys(),
|
||||
instance => instance.File,
|
||||
(instance, wrapper) => instance.Serialize(wrapper),
|
||||
data);
|
||||
}
|
||||
|
||||
// EditorParameters operations
|
||||
public static EditorParameters.ParamsWrapper ReadEditorParameters()
|
||||
{
|
||||
return ReadData(
|
||||
() => new EditorParameters(),
|
||||
instance => instance.File,
|
||||
(instance, file) => instance.Read(file));
|
||||
}
|
||||
|
||||
public static void WriteEditorParameters(EditorParameters.ParamsWrapper data)
|
||||
{
|
||||
WriteData(
|
||||
() => new EditorParameters(),
|
||||
instance => instance.File,
|
||||
(instance, wrapper) => instance.Serialize(wrapper),
|
||||
data);
|
||||
}
|
||||
|
||||
// DefaultLayouts operations
|
||||
public static DefaultLayouts.DefaultLayoutsListWrapper ReadDefaultLayouts()
|
||||
{
|
||||
return ReadData(
|
||||
() => new DefaultLayouts(),
|
||||
instance => instance.File,
|
||||
(instance, file) => instance.Read(file));
|
||||
}
|
||||
|
||||
public static void WriteDefaultLayouts(DefaultLayouts.DefaultLayoutsListWrapper data)
|
||||
{
|
||||
WriteData(
|
||||
() => new DefaultLayouts(),
|
||||
instance => instance.File,
|
||||
(instance, wrapper) => instance.Serialize(wrapper),
|
||||
data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -728,6 +728,13 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa
|
||||
{
|
||||
FancyZonesSettings::instance().LoadSettings();
|
||||
}
|
||||
else if (message == WM_PRIV_SAVE_EDITOR_PARAMETERS)
|
||||
{
|
||||
if (!EditorParameters::Save(m_workAreaConfiguration, m_dpiUnawareThread))
|
||||
{
|
||||
Logger::warn(L"Failed to save editor-parameters.json");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return DefWindowProc(window, message, wparam, lparam);
|
||||
|
||||
@@ -18,6 +18,7 @@ UINT WM_PRIV_DEFAULT_LAYOUTS_FILE_UPDATE;
|
||||
UINT WM_PRIV_SNAP_HOTKEY;
|
||||
UINT WM_PRIV_QUICK_LAYOUT_KEY;
|
||||
UINT WM_PRIV_SETTINGS_CHANGED;
|
||||
UINT WM_PRIV_SAVE_EDITOR_PARAMETERS;
|
||||
|
||||
std::once_flag init_flag;
|
||||
|
||||
@@ -40,5 +41,6 @@ void InitializeWinhookEventIds()
|
||||
WM_PRIV_SNAP_HOTKEY = RegisterWindowMessage(L"{72f4fd8e-23f1-43ab-bbbc-029363df9a84}");
|
||||
WM_PRIV_QUICK_LAYOUT_KEY = RegisterWindowMessage(L"{15baab3d-c67b-4a15-aFF0-13610e05e947}");
|
||||
WM_PRIV_SETTINGS_CHANGED = RegisterWindowMessage(L"{89ca3Daa-bf2d-4e73-9f3f-c60716364e27}");
|
||||
WM_PRIV_SAVE_EDITOR_PARAMETERS = RegisterWindowMessage(L"{d8f9c0e3-5d77-4e83-8a4f-7c704c2bfb4a}");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -16,5 +16,6 @@ extern UINT WM_PRIV_DEFAULT_LAYOUTS_FILE_UPDATE; // Scheduled when the watched d
|
||||
extern UINT WM_PRIV_SNAP_HOTKEY; // Scheduled when we receive a snap hotkey key down press
|
||||
extern UINT WM_PRIV_QUICK_LAYOUT_KEY; // Scheduled when we receive a key down press to quickly apply a layout
|
||||
extern UINT WM_PRIV_SETTINGS_CHANGED; // Scheduled when a watched settings file is updated
|
||||
extern UINT WM_PRIV_SAVE_EDITOR_PARAMETERS; // Scheduled to request saving editor-parameters.json
|
||||
|
||||
void InitializeWinhookEventIds();
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" />
|
||||
|
||||
|
||||
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.MSIX.1.7.20250829.1\build\Microsoft.Windows.SDK.BuildTools.MSIX.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.MSIX.1.7.20250829.1\build\Microsoft.Windows.SDK.BuildTools.MSIX.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<PropertyGroup Label="NuGet">
|
||||
<!-- Tell NuGet this is PackageReference style -->
|
||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
||||
|
||||
<!-- Tell NuGet we're a native project -->
|
||||
<NuGetTargetMoniker>native,Version=v0.0</NuGetTargetMoniker>
|
||||
|
||||
<!-- Tell NuGet we target Windows (use your existing WindowsTargetPlatformVersion) -->
|
||||
<NuGetTargetPlatformIdentifier>Windows</NuGetTargetPlatformIdentifier>
|
||||
<NuGetTargetPlatformVersion>$(WindowsTargetPlatformVersion)</NuGetTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<CppWinRTOptimized>true</CppWinRTOptimized>
|
||||
<CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge>
|
||||
@@ -37,9 +35,17 @@
|
||||
<EnablePreviewMsixTooling>true</EnablePreviewMsixTooling>
|
||||
<!-- MRT from windows app sdk will search for a pri file with the same name of the module before defaulting to resources.pri -->
|
||||
<ProjectPriFileName>PowerToys.PowerRename.pri</ProjectPriFileName>
|
||||
<RuntimeIdentifier>win10-x64;win10-arm64</RuntimeIdentifier>
|
||||
<WindowsAppSDKVerifyTransitiveDependencies>false</WindowsAppSDKVerifyTransitiveDependencies>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" GeneratePathProperty="true" />
|
||||
<PackageReference Include="Microsoft.Windows.CppWinRT" GeneratePathProperty="true" />
|
||||
<PackageReference Include="Microsoft.Windows.ImplementationLibrary" GeneratePathProperty="true" />
|
||||
<PackageReference Include="boost" GeneratePathProperty="true" />
|
||||
<PackageReference Include="boost_regex-vc143" GeneratePathProperty="true" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<Import Project="..\..\..\..\deps\spdlog.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
@@ -212,54 +218,10 @@
|
||||
<ResourceCompile Include="PowerRenameUI.rc" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.MSIX.1.7.20250829.1\build\Microsoft.Windows.SDK.BuildTools.MSIX.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.MSIX.1.7.20250829.1\build\Microsoft.Windows.SDK.BuildTools.MSIX.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" />
|
||||
<Import Project="..\..\..\..\packages\boost.1.87.0\build\boost.targets" Condition="Exists('..\..\..\..\packages\boost.1.87.0\build\boost.targets')" />
|
||||
<Import Project="..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets" Condition="Exists('..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets')" />
|
||||
</ImportGroup>
|
||||
<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.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.MSIX.1.7.20250829.1\build\Microsoft.Windows.SDK.BuildTools.MSIX.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.MSIX.1.7.20250829.1\build\Microsoft.Windows.SDK.BuildTools.MSIX.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.MSIX.1.7.20250829.1\build\Microsoft.Windows.SDK.BuildTools.MSIX.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.MSIX.1.7.20250829.1\build\Microsoft.Windows.SDK.BuildTools.MSIX.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\boost.1.87.0\build\boost.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\boost.1.87.0\build\boost.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets'))" />
|
||||
</Target>
|
||||
<Import Project="..\..\..\..\deps\spdlog.props" />
|
||||
<Target Name="AddWildCardItems" AfterTargets="BuildGenerateSources">
|
||||
<ItemGroup>
|
||||
<PRIResource Include="@(_WildCardPRIResource)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="boost" version="1.87.0" targetFramework="native" />
|
||||
<package id="boost_regex-vc143" version="1.87.0" targetFramework="native" />
|
||||
<package id="Microsoft.Web.WebView2" version="1.0.2903.40" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.SDK.BuildTools" version="10.0.26100.4188" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK" version="1.8.250907003" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Base" version="1.8.250831001" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Foundation" version="1.8.250906002" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.WinUI" version="1.8.250906003" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Runtime" version="1.8.250907003" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.DWrite" version="1.8.25090401" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.InteractiveExperiences" version="1.8.250906004" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Widgets" version="1.8.250904007" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.AI" version="1.8.37" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.SDK.BuildTools.MSIX" version="1.7.20250829.1" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -407,15 +407,18 @@ HRESULT GetDatedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source, SY
|
||||
hour12 = 12;
|
||||
}
|
||||
|
||||
// Order matters. Longer patterns are processed before any prefixes.
|
||||
// Years.
|
||||
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%04d"), L"$01", fileTime.wYear);
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$YYYY"), replaceTerm);
|
||||
|
||||
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", (fileTime.wYear % 100));
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$YY(?![A-Z])"), replaceTerm); // Negative lookahead prevents matching $YYY, $YYYY, or metadata patterns
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$YY"), replaceTerm);
|
||||
|
||||
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%d"), L"$01", (fileTime.wYear % 10));
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$Y(?![A-Z])"), replaceTerm); // Negative lookahead prevents matching $YY, $YYYY, or metadata patterns
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$Y"), replaceTerm);
|
||||
|
||||
// Months.
|
||||
GetDateFormatEx(localeName, NULL, &fileTime, L"MMMM", formattedDate, MAX_PATH, NULL);
|
||||
formattedDate[0] = towupper(formattedDate[0]);
|
||||
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%s"), L"$01", formattedDate);
|
||||
@@ -424,14 +427,15 @@ HRESULT GetDatedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source, SY
|
||||
GetDateFormatEx(localeName, NULL, &fileTime, L"MMM", formattedDate, MAX_PATH, NULL);
|
||||
formattedDate[0] = towupper(formattedDate[0]);
|
||||
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%s"), L"$01", formattedDate);
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$MMM(?!M)"), replaceTerm); // Negative lookahead prevents matching $MMMM
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$MMM"), replaceTerm);
|
||||
|
||||
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", fileTime.wMonth);
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$MM(?![A-Z])"), replaceTerm); // Negative lookahead prevents matching $MMM, $MMMM, or metadata patterns
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$MM"), replaceTerm);
|
||||
|
||||
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%d"), L"$01", fileTime.wMonth);
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$M(?![A-Z])"), replaceTerm); // Negative lookahead prevents matching $MM, $MMM, $MMMM, or metadata patterns
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$M"), replaceTerm);
|
||||
|
||||
// Days.
|
||||
GetDateFormatEx(localeName, NULL, &fileTime, L"dddd", formattedDate, MAX_PATH, NULL);
|
||||
formattedDate[0] = towupper(formattedDate[0]);
|
||||
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%s"), L"$01", formattedDate);
|
||||
@@ -440,19 +444,27 @@ HRESULT GetDatedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source, SY
|
||||
GetDateFormatEx(localeName, NULL, &fileTime, L"ddd", formattedDate, MAX_PATH, NULL);
|
||||
formattedDate[0] = towupper(formattedDate[0]);
|
||||
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%s"), L"$01", formattedDate);
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$DDD(?![A-Z])"), replaceTerm); // Negative lookahead prevents matching $DDDD or metadata patterns
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$DDD"), replaceTerm);
|
||||
|
||||
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", fileTime.wDay);
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$DD(?![A-Z])"), replaceTerm); // Negative lookahead prevents matching $DDD, $DDDD, or metadata patterns
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$DD"), replaceTerm);
|
||||
|
||||
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%d"), L"$01", fileTime.wDay);
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$D(?![A-Z])"), replaceTerm); // Negative lookahead prevents matching $DD, $DDD, $DDDD, or metadata patterns like $DATE_TAKEN_YYYY
|
||||
// $D overlaps with metadata patterns like $DATE_TAKEN_YYYY, so we use negative
|
||||
// lookahead to prevent matching those.
|
||||
res = regex_replace(
|
||||
res,
|
||||
std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$D(?!(ATE_TAKEN_|ESCRIPTION|OCUMENT_ID))"), /* #no-spell-check-line */
|
||||
replaceTerm);
|
||||
|
||||
// Time.
|
||||
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", hour12);
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$HH(?![A-Z])"), replaceTerm); // Negative lookahead prevents matching $HHH or metadata patterns
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$HH"), replaceTerm);
|
||||
|
||||
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%d"), L"$01", hour12);
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$H(?![A-Z])"), replaceTerm); // Negative lookahead prevents matching $HH or metadata patterns
|
||||
// $H overlaps with metadata's $HEIGHT, so we use negative lookahead to prevent
|
||||
// matching that.
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$H(?!(EIGHT))"), replaceTerm);
|
||||
|
||||
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%s"), L"$01", (fileTime.wHour < 12) ? L"AM" : L"PM");
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$TT"), replaceTerm);
|
||||
@@ -461,31 +473,31 @@ HRESULT GetDatedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source, SY
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$tt"), replaceTerm);
|
||||
|
||||
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", fileTime.wHour);
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$hh(?!h)"), replaceTerm); // Negative lookahead prevents matching $hhh
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$hh"), replaceTerm);
|
||||
|
||||
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%d"), L"$01", fileTime.wHour);
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$h(?!h)"), replaceTerm); // Negative lookahead prevents matching $hh
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$h"), replaceTerm);
|
||||
|
||||
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", fileTime.wMinute);
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$mm(?!m)"), replaceTerm); // Negative lookahead prevents matching $mmm
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$mm"), replaceTerm);
|
||||
|
||||
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%d"), L"$01", fileTime.wMinute);
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$m(?!m)"), replaceTerm); // Negative lookahead prevents matching $mm
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$m"), replaceTerm);
|
||||
|
||||
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", fileTime.wSecond);
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$ss(?!s)"), replaceTerm); // Negative lookahead prevents matching $sss
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$ss"), replaceTerm);
|
||||
|
||||
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%d"), L"$01", fileTime.wSecond);
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$s(?!s)"), replaceTerm); // Negative lookahead prevents matching $ss
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$s"), replaceTerm);
|
||||
|
||||
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%03d"), L"$01", fileTime.wMilliseconds);
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$fff(?!f)"), replaceTerm); // Negative lookahead prevents matching $ffff
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$fff"), replaceTerm);
|
||||
|
||||
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", fileTime.wMilliseconds / 10);
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$ff(?!f)"), replaceTerm); // Negative lookahead prevents matching $fff
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$ff"), replaceTerm);
|
||||
|
||||
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%d"), L"$01", fileTime.wMilliseconds / 100);
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$f(?!f)"), replaceTerm); // Negative lookahead prevents matching $ff or $fff
|
||||
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$f"), replaceTerm);
|
||||
|
||||
hr = StringCchCopy(result, cchMax, res.c_str());
|
||||
}
|
||||
|
||||
@@ -507,24 +507,26 @@ namespace HelpersTests
|
||||
return testTime;
|
||||
}
|
||||
|
||||
// Category 1: Tests for invalid patterns with extra characters (verify negative lookahead prevents wrong matching)
|
||||
// Category 1: Tests for patterns with extra characters. Verifies negative
|
||||
// lookahead doesn't cause issues with partially matched patterns and the
|
||||
// ordering of pattern matches is correct, i.e. longer patterns are matched
|
||||
// first.
|
||||
|
||||
TEST_METHOD(InvalidPattern_YYY_NotMatched)
|
||||
TEST_METHOD(ValidPattern_YYY_PartiallyMatched)
|
||||
{
|
||||
// Test $YYY (3 Y's) is not a valid pattern and should remain unchanged
|
||||
// Negative lookahead in $YY(?!Y) prevents matching $YYY
|
||||
// Test $YYY (3 Y's) is recognized as a valid pattern $YY plus a verbatim 'Y'
|
||||
SYSTEMTIME testTime = GetTestTime();
|
||||
wchar_t result[MAX_PATH] = { 0 };
|
||||
HRESULT hr = GetDatedFileName(result, MAX_PATH, L"file_$YYY", testTime);
|
||||
|
||||
Assert::IsTrue(SUCCEEDED(hr));
|
||||
Assert::AreEqual(L"file_$YYY", result); // $YYY is invalid, should remain unchanged
|
||||
Assert::AreEqual(L"file_24Y", result);
|
||||
}
|
||||
|
||||
TEST_METHOD(InvalidPattern_DDD_NotPartiallyMatched)
|
||||
TEST_METHOD(ValidPattern_DDD_Matched)
|
||||
{
|
||||
// Test that $DDD (short weekday) is not confused with $DD (2-digit day)
|
||||
// This verifies negative lookahead works correctly
|
||||
// Verifies that the matching of $DDD before $DD works correctly
|
||||
SYSTEMTIME testTime = GetTestTime();
|
||||
wchar_t result[MAX_PATH] = { 0 };
|
||||
HRESULT hr = GetDatedFileName(result, MAX_PATH, L"file_$DDD", testTime);
|
||||
@@ -533,9 +535,10 @@ namespace HelpersTests
|
||||
Assert::AreEqual(L"file_Fri", result); // Should be "Fri", not "15D"
|
||||
}
|
||||
|
||||
TEST_METHOD(InvalidPattern_MMM_NotPartiallyMatched)
|
||||
TEST_METHOD(ValidPattern_MMM_Matched)
|
||||
{
|
||||
// Test that $MMM (short month name) is not confused with $MM (2-digit month)
|
||||
// Verifies that the matching of $MMM before $MM works correctly
|
||||
SYSTEMTIME testTime = GetTestTime();
|
||||
wchar_t result[MAX_PATH] = { 0 };
|
||||
HRESULT hr = GetDatedFileName(result, MAX_PATH, L"file_$MMM", testTime);
|
||||
@@ -544,15 +547,16 @@ namespace HelpersTests
|
||||
Assert::AreEqual(L"file_Mar", result); // Should be "Mar", not "03M"
|
||||
}
|
||||
|
||||
TEST_METHOD(InvalidPattern_HHH_NotMatched)
|
||||
TEST_METHOD(ValidPattern_HHH_PartiallyMatched)
|
||||
{
|
||||
// Test $HHH (3 H's) is not valid and negative lookahead prevents $HH from matching
|
||||
// Test $HHH (3 H's) should match $HH and leave extra H unchanged
|
||||
// Also confirms that $HH is matched before $H
|
||||
SYSTEMTIME testTime = GetTestTime();
|
||||
wchar_t result[MAX_PATH] = { 0 };
|
||||
HRESULT hr = GetDatedFileName(result, MAX_PATH, L"file_$HHH", testTime);
|
||||
|
||||
Assert::IsTrue(SUCCEEDED(hr));
|
||||
Assert::AreEqual(L"file_$HHH", result); // Should remain unchanged
|
||||
Assert::AreEqual(L"file_02H", result);
|
||||
}
|
||||
|
||||
TEST_METHOD(SeparatedPatterns_SingleY)
|
||||
@@ -669,9 +673,9 @@ namespace HelpersTests
|
||||
Assert::AreEqual(E_INVALIDARG, hr);
|
||||
}
|
||||
|
||||
// Category 4: Tests to explicitly verify negative lookahead is working
|
||||
// Category 4: Tests to explicitly verify execution order
|
||||
|
||||
TEST_METHOD(NegativeLookahead_YearNotMatchedInYYYY)
|
||||
TEST_METHOD(ExecutionOrder_YearNotMatchedInYYYY)
|
||||
{
|
||||
// Verify $Y doesn't match when part of $YYYY
|
||||
SYSTEMTIME testTime = GetTestTime();
|
||||
@@ -682,9 +686,9 @@ namespace HelpersTests
|
||||
Assert::AreEqual(L"file_2024", result); // Should be "2024", not "202Y"
|
||||
}
|
||||
|
||||
TEST_METHOD(NegativeLookahead_MonthNotMatchedInMMM)
|
||||
TEST_METHOD(ExecutionOrder_MonthNotMatchedInMMM)
|
||||
{
|
||||
// Verify $M doesn't match when part of $MMM
|
||||
// Verify $M or $MM don't match when $MMM is given
|
||||
SYSTEMTIME testTime = GetTestTime();
|
||||
wchar_t result[MAX_PATH] = { 0 };
|
||||
HRESULT hr = GetDatedFileName(result, MAX_PATH, L"file_$MMM", testTime);
|
||||
@@ -693,9 +697,9 @@ namespace HelpersTests
|
||||
Assert::AreEqual(L"file_Mar", result); // Should be "Mar", not "3ar"
|
||||
}
|
||||
|
||||
TEST_METHOD(NegativeLookahead_DayNotMatchedInDDDD)
|
||||
TEST_METHOD(ExecutionOrder_DayNotMatchedInDDDD)
|
||||
{
|
||||
// Verify $D doesn't match when part of $DDDD
|
||||
// Verify $D or $DD don't match when $DDDD is given
|
||||
SYSTEMTIME testTime = GetTestTime();
|
||||
wchar_t result[MAX_PATH] = { 0 };
|
||||
HRESULT hr = GetDatedFileName(result, MAX_PATH, L"file_$DDDD", testTime);
|
||||
@@ -704,7 +708,7 @@ namespace HelpersTests
|
||||
Assert::AreEqual(L"file_Friday", result); // Should be "Friday", not "15riday"
|
||||
}
|
||||
|
||||
TEST_METHOD(NegativeLookahead_HourNotMatchedInHH)
|
||||
TEST_METHOD(ExecutionOrder_HourNotMatchedInHH)
|
||||
{
|
||||
// Verify $H doesn't match when part of $HH
|
||||
// Note: $HH is 12-hour format, so 14:00 (2 PM) displays as "02"
|
||||
@@ -716,9 +720,9 @@ namespace HelpersTests
|
||||
Assert::AreEqual(L"file_02", result); // 14:00 in 12-hour format is "02 PM"
|
||||
}
|
||||
|
||||
TEST_METHOD(NegativeLookahead_MillisecondNotMatchedInFFF)
|
||||
TEST_METHOD(ExecutionOrder_MillisecondNotMatchedInFFF)
|
||||
{
|
||||
// Verify $f doesn't match when part of $fff
|
||||
// Verify $f or $ff don't match when $fff is given
|
||||
SYSTEMTIME testTime = GetTestTime();
|
||||
wchar_t result[MAX_PATH] = { 0 };
|
||||
HRESULT hr = GetDatedFileName(result, MAX_PATH, L"file_$fff", testTime);
|
||||
@@ -762,5 +766,68 @@ namespace HelpersTests
|
||||
Assert::IsTrue(SUCCEEDED(hr));
|
||||
Assert::AreEqual(L"15-15-Fri-Friday", result);
|
||||
}
|
||||
|
||||
// Category 6: Specific bug fixes and collision avoidance
|
||||
|
||||
TEST_METHOD(BugFix_DDT_AllowsSuffixT)
|
||||
{
|
||||
// #44202 - $DDT should be allowed and matched as $DD plus verbatim 'T'. It
|
||||
// was previously blocked due to the negative lookahead for any capital
|
||||
// letter after $DD.
|
||||
SYSTEMTIME testTime = GetTestTime();
|
||||
wchar_t result[MAX_PATH] = { 0 };
|
||||
HRESULT hr = GetDatedFileName(result, MAX_PATH, L"file_$DDT", testTime);
|
||||
|
||||
Assert::IsTrue(SUCCEEDED(hr));
|
||||
Assert::AreEqual(L"file_15T", result);
|
||||
}
|
||||
|
||||
TEST_METHOD(RelaxedConstraint_VerbatimCapitalAfterPatterns)
|
||||
{
|
||||
// Verify that patterns can be followed by capital letters that are not part
|
||||
// of longer patterns, e.g., $DDC should match $DD + 'C'.
|
||||
SYSTEMTIME testTime = GetTestTime();
|
||||
wchar_t result[MAX_PATH] = { 0 };
|
||||
HRESULT hr = GetDatedFileName(result, MAX_PATH, L"file_$YYYYA_$MMB_$DDC", testTime); /* #no-spell-check-line */
|
||||
|
||||
Assert::IsTrue(SUCCEEDED(hr));
|
||||
Assert::AreEqual(L"file_2024A_03B_15C", result);
|
||||
}
|
||||
|
||||
TEST_METHOD(Collision_DateTaken_Protected)
|
||||
{
|
||||
// Verify that date patterns do not collide with metadata patterns like
|
||||
// DATE_TAKEN_YYYY.
|
||||
SYSTEMTIME testTime = GetTestTime();
|
||||
wchar_t result[MAX_PATH] = { 0 };
|
||||
HRESULT hr = GetDatedFileName(result, MAX_PATH, L"file_$DATE_TAKEN_YYYY", testTime);
|
||||
|
||||
Assert::IsTrue(SUCCEEDED(hr));
|
||||
Assert::AreEqual(L"file_$DATE_TAKEN_YYYY", result); // Not replaced
|
||||
}
|
||||
|
||||
TEST_METHOD(Collision_Height_Protected)
|
||||
{
|
||||
// Verify that HEIGHT metadata pattern does not collide with date pattern $H.
|
||||
SYSTEMTIME testTime = GetTestTime();
|
||||
wchar_t result[MAX_PATH] = { 0 };
|
||||
HRESULT hr = GetDatedFileName(result, MAX_PATH, L"file_$HEIGHT", testTime);
|
||||
|
||||
Assert::IsTrue(SUCCEEDED(hr));
|
||||
Assert::AreEqual(L"file_$HEIGHT", result); // Not replaced
|
||||
}
|
||||
|
||||
TEST_METHOD(Collision_SafeSuffix_Deer)
|
||||
{
|
||||
// Verifies that patterns can be safely followed by certain suffix letters as
|
||||
// long as they don't match a longer pattern. $DEER should be matched as
|
||||
// $D + 'EER'
|
||||
SYSTEMTIME testTime = GetTestTime();
|
||||
wchar_t result[MAX_PATH] = { 0 };
|
||||
HRESULT hr = GetDatedFileName(result, MAX_PATH, L"file_$DEER", testTime);
|
||||
|
||||
Assert::IsTrue(SUCCEEDED(hr));
|
||||
Assert::AreEqual(L"file_15EER", result);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "general_settings.h"
|
||||
#include "auto_start_helper.h"
|
||||
#include "tray_icon.h"
|
||||
#include "quick_access_host.h"
|
||||
#include "Generated files/resource.h"
|
||||
#include "hotkey_conflict_detector.h"
|
||||
|
||||
@@ -72,6 +73,8 @@ static bool download_updates_automatically = true;
|
||||
static bool show_whats_new_after_updates = true;
|
||||
static bool enable_experimentation = true;
|
||||
static bool enable_warnings_elevated_apps = true;
|
||||
static bool enable_quick_access = true;
|
||||
static PowerToysSettings::HotkeyObject quick_access_shortcut;
|
||||
static DashboardSortOrder dashboard_sort_order = DashboardSortOrder::Alphabetical;
|
||||
static json::JsonObject ignored_conflict_properties = create_default_ignored_conflict_properties();
|
||||
|
||||
@@ -105,6 +108,8 @@ json::JsonObject GeneralSettings::to_json()
|
||||
result.SetNamedValue(L"dashboard_sort_order", json::value(static_cast<int>(dashboardSortOrder)));
|
||||
result.SetNamedValue(L"is_admin", json::value(isAdmin));
|
||||
result.SetNamedValue(L"enable_warnings_elevated_apps", json::value(enableWarningsElevatedApps));
|
||||
result.SetNamedValue(L"enable_quick_access", json::value(enableQuickAccess));
|
||||
result.SetNamedValue(L"quick_access_shortcut", quickAccessShortcut.get_json());
|
||||
result.SetNamedValue(L"theme", json::value(theme));
|
||||
result.SetNamedValue(L"system_theme", json::value(systemTheme));
|
||||
result.SetNamedValue(L"powertoys_version", json::value(powerToysVersion));
|
||||
@@ -127,6 +132,11 @@ json::JsonObject load_general_settings()
|
||||
show_whats_new_after_updates = loaded.GetNamedBoolean(L"show_whats_new_after_updates", true);
|
||||
enable_experimentation = loaded.GetNamedBoolean(L"enable_experimentation", true);
|
||||
enable_warnings_elevated_apps = loaded.GetNamedBoolean(L"enable_warnings_elevated_apps", true);
|
||||
enable_quick_access = loaded.GetNamedBoolean(L"enable_quick_access", true);
|
||||
if (json::has(loaded, L"quick_access_shortcut", json::JsonValueType::Object))
|
||||
{
|
||||
quick_access_shortcut = PowerToysSettings::HotkeyObject::from_json(loaded.GetNamedObject(L"quick_access_shortcut"));
|
||||
}
|
||||
dashboard_sort_order = parse_dashboard_sort_order(loaded, dashboard_sort_order);
|
||||
|
||||
if (json::has(loaded, L"ignored_conflict_properties", json::JsonValueType::Object))
|
||||
@@ -153,6 +163,8 @@ GeneralSettings get_general_settings()
|
||||
.isRunElevated = run_as_elevated,
|
||||
.isAdmin = is_user_admin,
|
||||
.enableWarningsElevatedApps = enable_warnings_elevated_apps,
|
||||
.enableQuickAccess = enable_quick_access,
|
||||
.quickAccessShortcut = quick_access_shortcut,
|
||||
.showNewUpdatesToastNotification = show_new_updates_toast_notification,
|
||||
.downloadUpdatesAutomatically = download_updates_automatically && is_user_admin,
|
||||
.showWhatsNewAfterUpdates = show_whats_new_after_updates,
|
||||
@@ -178,11 +190,47 @@ GeneralSettings get_general_settings()
|
||||
|
||||
void apply_general_settings(const json::JsonObject& general_configs, bool save)
|
||||
{
|
||||
std::wstring old_settings_json_string;
|
||||
if (save)
|
||||
{
|
||||
old_settings_json_string = get_general_settings().to_json().Stringify().c_str();
|
||||
}
|
||||
|
||||
Logger::info(L"apply_general_settings: {}", std::wstring{ general_configs.ToString() });
|
||||
run_as_elevated = general_configs.GetNamedBoolean(L"run_elevated", false);
|
||||
|
||||
enable_warnings_elevated_apps = general_configs.GetNamedBoolean(L"enable_warnings_elevated_apps", true);
|
||||
|
||||
bool new_enable_quick_access = general_configs.GetNamedBoolean(L"enable_quick_access", true);
|
||||
Logger::info(L"apply_general_settings: enable_quick_access={}, new_enable_quick_access={}", enable_quick_access, new_enable_quick_access);
|
||||
|
||||
PowerToysSettings::HotkeyObject new_quick_access_shortcut;
|
||||
if (json::has(general_configs, L"quick_access_shortcut", json::JsonValueType::Object))
|
||||
{
|
||||
new_quick_access_shortcut = PowerToysSettings::HotkeyObject::from_json(general_configs.GetNamedObject(L"quick_access_shortcut"));
|
||||
}
|
||||
|
||||
auto hotkey_equals = [](const PowerToysSettings::HotkeyObject& a, const PowerToysSettings::HotkeyObject& b) {
|
||||
return a.get_code() == b.get_code() &&
|
||||
a.get_modifiers() == b.get_modifiers();
|
||||
};
|
||||
|
||||
if (enable_quick_access != new_enable_quick_access || !hotkey_equals(quick_access_shortcut, new_quick_access_shortcut))
|
||||
{
|
||||
enable_quick_access = new_enable_quick_access;
|
||||
quick_access_shortcut = new_quick_access_shortcut;
|
||||
|
||||
if (enable_quick_access)
|
||||
{
|
||||
QuickAccessHost::start();
|
||||
}
|
||||
else
|
||||
{
|
||||
QuickAccessHost::stop();
|
||||
}
|
||||
update_quick_access_hotkey(enable_quick_access, quick_access_shortcut);
|
||||
}
|
||||
|
||||
show_new_updates_toast_notification = general_configs.GetNamedBoolean(L"show_new_updates_toast_notification", true);
|
||||
|
||||
download_updates_automatically = general_configs.GetNamedBoolean(L"download_updates_automatically", true);
|
||||
@@ -321,8 +369,12 @@ void apply_general_settings(const json::JsonObject& general_configs, bool save)
|
||||
if (save)
|
||||
{
|
||||
GeneralSettings save_settings = get_general_settings();
|
||||
PTSettingsHelper::save_general_settings(save_settings.to_json());
|
||||
Trace::SettingsChanged(save_settings);
|
||||
std::wstring new_settings_json_string = save_settings.to_json().Stringify().c_str();
|
||||
if (old_settings_json_string != new_settings_json_string)
|
||||
{
|
||||
PTSettingsHelper::save_general_settings(save_settings.to_json());
|
||||
Trace::SettingsChanged(save_settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -412,3 +464,5 @@ void start_enabled_powertoys()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/utils/json.h>
|
||||
#include <common/SettingsAPI/settings_objects.h>
|
||||
|
||||
enum class DashboardSortOrder
|
||||
{
|
||||
@@ -18,6 +19,8 @@ struct GeneralSettings
|
||||
bool isRunElevated;
|
||||
bool isAdmin;
|
||||
bool enableWarningsElevatedApps;
|
||||
bool enableQuickAccess;
|
||||
PowerToysSettings::HotkeyObject quickAccessShortcut;
|
||||
bool showNewUpdatesToastNotification;
|
||||
bool downloadUpdatesAutomatically;
|
||||
bool showWhatsNewAfterUpdates;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user