Compare commits

..

1 Commits

Author SHA1 Message Date
Clint Rutkas
6b34038f2b Added open tag to default expand open
Updated README to reflect changes in installation instructions.
2025-10-23 16:20:52 +00:00
161 changed files with 942 additions and 7849 deletions

View File

@@ -321,10 +321,3 @@ REGSTR
# Misc Win32 APIs and PInvokes
INVOKEIDLIST
# PowerRename metadata pattern abbreviations (used in tests and regex patterns)
DDDD
FFF
HHH
riday
YYY

View File

@@ -97,7 +97,6 @@ atl
ATX
ATRIOX
aumid
authenticode
Authenticode
AUTOBUDDY
AUTOCHECKBOX
@@ -143,7 +142,6 @@ bmi
BNumber
BODGY
BOklab
Bootstrappers
BOOTSTRAPPERINSTALLFOLDER
BOTTOMALIGN
boxmodel
@@ -171,12 +169,9 @@ BYPOSITION
CALCRECT
CALG
callbackptr
cabstr
calpwstr
caub
Cangjie
CANRENAME
Carlseibert
Canvascustomlayout
CAPTUREBLT
CAPTURECHANGED
@@ -282,7 +277,6 @@ cpptools
cppvsdbg
cppwinrt
createdump
creativecommons
CREATEPROCESS
CREATESCHEDULEDTASK
CREATESTRUCT
@@ -345,7 +339,6 @@ Deact
debugbreak
decryptor
Dedup
dfx
Deduplicator
Deeplink
DEFAULTBOOTSTRAPPERINSTALLFOLDER
@@ -519,12 +512,10 @@ EXTRINSICPROPERTIES
eyetracker
FANCYZONESDRAWLAYOUTTEST
FANCYZONESEDITOR
FNumber
FARPROC
fdx
fesf
FFFF
Figma
FILEEXPLORER
fileexploreraddons
fileexplorerpreview
@@ -703,7 +694,6 @@ HTCLIENT
hthumbnail
HTOUCHINPUT
HTTRANSPARENT
hutchinsoniana
HVal
HValue
Hvci
@@ -725,9 +715,7 @@ IDCANCEL
IDD
idk
idl
IIM
idlist
ifd
IDOK
IDOn
IDR
@@ -744,7 +732,6 @@ Ijwhost
ILD
IMAGEHLP
IMAGERESIZERCONTEXTMENU
IPTC
IMAGERESIZEREXT
imageresizerinput
imageresizersettings
@@ -763,7 +750,7 @@ INITDIALOG
INITGUID
INITTOLOGFONTSTRUCT
INLINEPREFIX
inlines
Inlines
INPC
inproc
INPUTHARDWARE
@@ -884,7 +871,6 @@ LOCKTYPE
LOGFONT
LOGFONTW
logon
lon
LOGMSG
LOGPIXELSX
LOGPIXELSY
@@ -976,7 +962,6 @@ MENUITEMINFOW
MERGECOPY
MERGEPAINT
Metadatas
metadatamatters
metafile
mfc
Mgmt
@@ -1044,7 +1029,6 @@ msiexec
MSIFASTINSTALL
MSIHANDLE
MSIRESTARTMANAGERCONTROL
MSIs
msixbundle
MSIXCA
MSLLHOOKSTRUCT
@@ -1136,7 +1120,6 @@ NONCLIENTMETRICSW
NONELEVATED
nonspace
nonstd
nullrefs
NOOWNERZORDER
NOPARENTNOTIFY
NOPREFIX
@@ -1293,7 +1276,6 @@ pnid
PNMLINK
Poc
Podcasts
Photoshop
POINTERID
POINTERUPDATE
Pokedex
@@ -1496,7 +1478,6 @@ sacl
safeprojectname
SAMEKEYPREVIOUSLYMAPPED
SAMESHORTCUTPREVIOUSLYMAPPED
samsung
sancov
SAVEFAILED
scanled
@@ -1859,7 +1840,6 @@ USEINSTALLERFORTEST
USESHOWWINDOW
USESTDHANDLES
USRDLL
utm
UType
uuidv
uwp
@@ -1949,7 +1929,6 @@ wgpocpl
WHEREID
wic
wifi
wikimedia
wikipedia
WIL
winapi
@@ -2044,9 +2023,7 @@ XAxis
XButton
xclip
xcopy
xap
XDeployment
XDimension
xdf
XDocument
XElement
@@ -2064,7 +2041,6 @@ xsi
XSpeed
XStr
xstyler
xmp
XTimer
XUP
XVIRTUALSCREEN
@@ -2072,7 +2048,6 @@ xxxxxx
YAxis
ycombinator
YIncrement
YDimension
yinle
yinyue
YPels

View File

@@ -0,0 +1,53 @@
{
"Version": "1.0.0",
"UseMinimatch": false,
"SignBatches": [
{
"MatchedPath": [
"PowerToysSetupCustomActionsVNext.dll",
"SilentFilesInUseBAFunction.dll",
"PowerToys*Setup-*.exe",
"PowerToys*Setup-*.msi"
],
"SigningInfo": {
"Operations": [
{
"KeyCode": "CP-230012",
"OperationSetCode": "SigntoolSign",
"Parameters": [
{
"parameterName": "OpusName",
"parameterValue": "Microsoft"
},
{
"parameterName": "OpusInfo",
"parameterValue": "http://www.microsoft.com"
},
{
"parameterName": "FileDigest",
"parameterValue": "/fd \"SHA256\""
},
{
"parameterName": "PageHash",
"parameterValue": "/NPH"
},
{
"parameterName": "TimeStamp",
"parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
}
],
"ToolName": "sign",
"ToolVersion": "1.0"
},
{
"KeyCode": "CP-230012",
"OperationSetCode": "SigntoolVerify",
"Parameters": [],
"ToolName": "sign",
"ToolVersion": "1.0"
}
]
}
}
]
}

View File

@@ -512,6 +512,14 @@ jobs:
versionNumber: ${{ parameters.versionNumber }}
additionalBuildOptions: ${{ parameters.additionalBuildOptions }}
- template: steps-build-installer-vnext.yml
parameters:
codeSign: ${{ parameters.codeSign }}
signingIdentity: ${{ parameters.signingIdentity }}
versionNumber: ${{ parameters.versionNumber }}
additionalBuildOptions: ${{ parameters.additionalBuildOptions }}
buildUserInstaller: true # NOTE: This is the distinction between the above and below rules
# This saves ~1GiB per architecture. We won't need these later.
# Removes:
# - All .pdb files from any static libs .libs (which were only used during linking)

View File

@@ -2,6 +2,9 @@ parameters:
- name: versionNumber
type: string
default: "0.0.1"
- name: buildUserInstaller
type: boolean
default: false
- name: codeSign
type: boolean
default: false
@@ -22,26 +25,43 @@ steps:
arguments: 'install --global wix --version 5.0.2'
- pwsh: |-
Write-Host "##vso[task.setvariable variable=InstallerMachineRoot]installer\PowerToysSetupVNext\$(BuildPlatform)\$(BuildConfiguration)\MachineSetup"
Write-Host "##vso[task.setvariable variable=InstallerUserRoot]installer\PowerToysSetupVNext\$(BuildPlatform)\$(BuildConfiguration)\UserSetup"
Write-Host "##vso[task.setvariable variable=InstallerMachineBasename]PowerToysSetup-${{ parameters.versionNumber }}-$(BuildPlatform)"
Write-Host "##vso[task.setvariable variable=InstallerUserBasename]PowerToysUserSetup-${{ parameters.versionNumber }}-$(BuildPlatform)"
displayName: Prepare Installer variables
& git clean -xfd -e *exe -- .\installer\
displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Clean installer to reduce cross-contamination
- pwsh: |-
# Determine whether this is a per-user build
$IsPerUser = $${{ parameters.buildUserInstaller }}
# Build slug used to locate the artifacts
$InstallerBuildSlug = if ($IsPerUser) { 'UserSetup' } else { 'MachineSetup' }
# VNext bundle folder; base name intentionally omits the VNext suffix
$InstallerFolder = 'PowerToysSetupVNext'
if ($IsPerUser) {
$InstallerBasename = "PowerToysUserSetup-${{ parameters.versionNumber }}-$(BuildPlatform)"
}
else {
$InstallerBasename = "PowerToysSetup-${{ parameters.versionNumber }}-$(BuildPlatform)"
}
# Export variables for downstream steps
Write-Host "##vso[task.setvariable variable=InstallerBuildSlug]$InstallerBuildSlug"
Write-Host "##vso[task.setvariable variable=InstallerRelativePath]$(BuildPlatform)\$(BuildConfiguration)\$InstallerBuildSlug"
Write-Host "##vso[task.setvariable variable=InstallerBasename]$InstallerBasename"
Write-Host "##vso[task.setvariable variable=InstallerFolder]$InstallerFolder"
displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Prepare Installer variables
# This dll needs to be built and signed before building the MSI.
# The Custom Actions project contains a pre-build event that prepares the .wxs files
# by filling them out with all our components. We pass RunBuildEvents=true to force
# that logic to run.
- task: VSBuild@1
displayName: Build Shared Support DLLs
displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Build PowerToysSetupCustomActionsVNext
inputs:
solution: "**/installer/PowerToysSetup.sln"
vsVersion: 17.0
msbuildArgs: >-
/t:PowerToysSetupCustomActionsVNext;SilentFilesInUseBAFunction
/p:RunBuildEvents=true;RestorePackagesConfig=true;CIBuild=true
/t:PowerToysSetupCustomActionsVNext
/p:RunBuildEvents=true;PerUser=${{parameters.buildUserInstaller}};RestorePackagesConfig=true;CIBuild=true
-restore -graph
/bl:$(LogOutputDirectory)\installer-actions.binlog
/bl:$(LogOutputDirectory)\installer-$(InstallerBuildSlug)-actions.binlog
${{ parameters.additionalBuildOptions }}
platform: $(BuildPlatform)
configuration: $(BuildConfiguration)
@@ -50,53 +70,28 @@ steps:
maximumCpuCount: true
- ${{ if eq(parameters.codeSign, true) }}:
- template: steps-esrp-sign-files-authenticode.yml
- template: steps-esrp-signing.yml
parameters:
displayName: Sign Shared Support DLLs
displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Sign PowerToysSetupCustomActionsVNext
signingIdentity: ${{ parameters.signingIdentity }}
folder: 'installer'
pattern: |-
**/PowerToysSetupCustomActionsVNext.dll
**/SilentFilesInUseBAFunction.dll
inputs:
FolderPath: 'installer/PowerToysSetupCustomActionsVNext/$(InstallerRelativePath)'
signType: batchSigning
batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_installer.json'
ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml'
## INSTALLER START
#### MSI BUILDING AND SIGNING
#
# The MSI build contains code that reverts the .wxs files to their in-tree versions.
# This is only supposed to happen during local builds. Since this build system is
# supposed to run side by side--machine and then user--we do NOT want to destroy
# the .wxs files. Therefore, we pass RunBuildEvents=false to suppress all of that
# logic.
#
# We pass BuildProjectReferences=false so that it does not recompile the DLLs we just built.
# We only pass -restore on the first one because the second run should already have all
# of the dependencies.
- task: VSBuild@1
displayName: 💻 Build VNext MSI
displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Build VNext MSI
inputs:
solution: "**/installer/PowerToysSetup.sln"
vsVersion: 17.0
msbuildArgs: >-
-restore
/t:PowerToysInstallerVNext
/p:RunBuildEvents=false;PerUser=false;BuildProjectReferences=false;CIBuild=true
/bl:$(LogOutputDirectory)\installer-machine-msi.binlog
${{ parameters.additionalBuildOptions }}
platform: $(BuildPlatform)
configuration: $(BuildConfiguration)
clean: false # don't undo our hard work above by deleting the CustomActions dll
msbuildArchitecture: x64
maximumCpuCount: true
- task: VSBuild@1
displayName: 👤 Build VNext MSI
inputs:
solution: "**/installer/PowerToysSetup.sln"
vsVersion: 17.0
msbuildArgs: >-
/t:PowerToysInstallerVNext
/p:RunBuildEvents=false;PerUser=true;BuildProjectReferences=false;CIBuild=true
/bl:$(LogOutputDirectory)\installer-user-msi.binlog
/p:RunBuildEvents=false;PerUser=${{parameters.buildUserInstaller}};BuildProjectReferences=false;CIBuild=true
/bl:$(LogOutputDirectory)\installer-$(InstallerBuildSlug)-msi.binlog
${{ parameters.additionalBuildOptions }}
platform: $(BuildPlatform)
configuration: $(BuildConfiguration)
@@ -105,66 +100,77 @@ steps:
maximumCpuCount: true
- script: |-
wix msi decompile $(InstallerMachineRoot)\$(InstallerMachineBasename).msi -x $(build.sourcesdirectory)\extractedMachineMsi
wix msi decompile $(InstallerUserRoot)\$(InstallerUserBasename).msi -x $(build.sourcesdirectory)\extractedUserMsi
dir $(build.sourcesdirectory)\extractedMachineMsi
dir $(build.sourcesdirectory)\extractedUserMsi
displayName: "WiX5: Extract and verify MSIs"
wix msi decompile installer\$(InstallerFolder)\$(InstallerRelativePath)\$(InstallerBasename).msi -x $(build.sourcesdirectory)\extractedMsi
dir $(build.sourcesdirectory)\extractedMsi
displayName: "${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} WiX5: Extract and verify MSI"
# Check if deps.json files don't reference different dll versions.
- pwsh: |-
& '.pipelines/verifyDepsJsonLibraryVersions.ps1' -targetDir '$(build.sourcesdirectory)\extractedMachineMsi\File'
& '.pipelines/verifyDepsJsonLibraryVersions.ps1' -targetDir '$(build.sourcesdirectory)\extractedUserMsi\File'
displayName: Audit deps.json in MSI extracted files
& '.pipelines/verifyDepsJsonLibraryVersions.ps1' -targetDir '$(build.sourcesdirectory)\extractedMsi\File'
displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Audit deps.json in MSI extracted files
- ${{ if eq(parameters.codeSign, true) }}:
- pwsh: |-
& .pipelines/versionAndSignCheck.ps1 -targetDir '$(build.sourcesdirectory)\extractedMachineMsi\File'
& .pipelines/versionAndSignCheck.ps1 -targetDir '$(build.sourcesdirectory)\extractedMachineMsi\Binary'
& .pipelines/versionAndSignCheck.ps1 -targetDir '$(build.sourcesdirectory)\extractedUserMsi\File'
& .pipelines/versionAndSignCheck.ps1 -targetDir '$(build.sourcesdirectory)\extractedUserMsi\Binary'
git clean -xfd ./extractedMachineMsi ./extractedUserMsi
displayName: Verify all binaries are signed and versioned
& .pipelines/versionAndSignCheck.ps1 -targetDir '$(build.sourcesdirectory)\extractedMsi\File'
& .pipelines/versionAndSignCheck.ps1 -targetDir '$(build.sourcesdirectory)\extractedMsi\Binary'
git clean -xfd ./extractedMsi
displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Verify all binaries are signed and versioned
- template: steps-esrp-sign-files-authenticode.yml
- template: steps-esrp-signing.yml
parameters:
displayName: Sign VNext MSIs
displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Sign VNext MSI
signingIdentity: ${{ parameters.signingIdentity }}
folder: 'installer'
pattern: '**/PowerToys*Setup-*.msi'
inputs:
FolderPath: 'installer/$(InstallerFolder)/$(InstallerRelativePath)'
signType: batchSigning
batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_installer.json'
ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml'
#### END MSI
#### BOOTSTRAP BUILDING AND SIGNING
# We pass BuildProjectReferences=false so that it does not recompile the DLLs we just built.
# We only pass -restore on the first one because the second run should already have all
# of the dependencies.
#### BUILDING AND SIGNING SilentFilesInUseBAFunction DLL
- task: VSBuild@1
displayName: 💻 Build VNext Bootstrapper
displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Build SilentFilesInUseBAFunction
inputs:
solution: "**/installer/PowerToysSetup.sln"
vsVersion: 17.0
msbuildArgs: >-
/t:SilentFilesInUseBAFunction
/p:RunBuildEvents=true;PerUser=${{parameters.buildUserInstaller}};RestorePackagesConfig=true;CIBuild=true
-restore -graph
/bl:$(LogOutputDirectory)\installer-$(InstallerBuildSlug)-SilentFilesInUseBAFunction.binlog
${{ parameters.additionalBuildOptions }}
platform: $(BuildPlatform)
configuration: $(BuildConfiguration)
clean: false # don't undo our hard work above by deleting the msi
msbuildArchitecture: x64
maximumCpuCount: true
- ${{ if eq(parameters.codeSign, true) }}:
- template: steps-esrp-signing.yml
parameters:
displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Sign SilentFilesInUseBAFunction
signingIdentity: ${{ parameters.signingIdentity }}
inputs:
FolderPath: 'installer/$(BuildPlatform)/$(BuildConfiguration)'
signType: batchSigning
batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_installer.json'
ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml'
#### END BUILDING AND SIGNING SilentFilesInUseBAFunction DLL
#### BOOTSTRAP BUILDING AND SIGNING
- task: VSBuild@1
displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Build VNext Bootstrapper
inputs:
solution: "**/installer/PowerToysSetup.sln"
vsVersion: 17.0
msbuildArgs: >-
-restore
/t:PowerToysBootstrapperVNext
/p:PerUser=false;BuildProjectReferences=false;CIBuild=true
/bl:$(LogOutputDirectory)\installer-machine-bootstrapper.binlog
${{ parameters.additionalBuildOptions }}
platform: $(BuildPlatform)
configuration: $(BuildConfiguration)
clean: false # don't undo our hard work above by deleting the MSI nor SilentFilesInUseBAFunction
msbuildArchitecture: x64
maximumCpuCount: true
- task: VSBuild@1
displayName: 👤 Build VNext Bootstrapper
inputs:
solution: "**/installer/PowerToysSetup.sln"
vsVersion: 17.0
msbuildArgs: >-
/t:PowerToysBootstrapperVNext
/p:PerUser=true;BuildProjectReferences=false;CIBuild=true
/bl:$(LogOutputDirectory)\installer-user-bootstrapper.binlog
/p:PerUser=${{parameters.buildUserInstaller}};CIBuild=true
/bl:$(LogOutputDirectory)\installer-$(InstallerBuildSlug)-bootstrapper.binlog
-restore -graph
${{ parameters.additionalBuildOptions }}
platform: $(BuildPlatform)
configuration: $(BuildConfiguration)
@@ -175,41 +181,54 @@ steps:
# The entirety of bundle unpacking/re-packing is unnecessary if we are not code signing it.
- ${{ if eq(parameters.codeSign, true) }}:
- script: |-
wix burn detach $(InstallerMachineRoot)\$(InstallerMachineBasename).exe -engine installer\machine-engine.exe
wix burn detach $(InstallerUserRoot)\$(InstallerUserBasename).exe -engine installer\user-engine.exe
displayName: "WiX5: Extract Engines from Bundles"
wix burn detach installer\$(InstallerFolder)\$(InstallerRelativePath)\$(InstallerBasename).exe -engine installer\engine.exe
displayName: "${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} WiX5: Extract Engine from Bundle"
- template: steps-esrp-sign-files-authenticode.yml
- template: steps-esrp-signing.yml
parameters:
displayName: Sign WiX Engines
displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Sign WiX Engine
signingIdentity: ${{ parameters.signingIdentity }}
folder: "installer"
pattern: '*-engine.exe'
inputs:
FolderPath: "installer"
Pattern: engine.exe
signConfigType: inlineSignParams
inlineOperation: |
[
{
"KeyCode": "CP-230012",
"OperationCode": "SigntoolSign",
"Parameters": {
"OpusName": "Microsoft",
"OpusInfo": "http://www.microsoft.com",
"FileDigest": "/fd \"SHA256\"",
"PageHash": "/NPH",
"TimeStamp": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
},
"ToolName": "sign",
"ToolVersion": "1.0"
},
{
"KeyCode": "CP-230012",
"OperationCode": "SigntoolVerify",
"Parameters": {},
"ToolName": "sign",
"ToolVersion": "1.0"
}
]
- script: |-
wix burn reattach $(InstallerMachineRoot)\$(InstallerMachineBasename).exe -engine installer\machine-engine.exe -o $(InstallerMachineRoot)\$(InstallerMachineBasename).exe
wix burn reattach $(InstallerUserRoot)\$(InstallerUserBasename).exe -engine installer\user-engine.exe -o $(InstallerUserRoot)\$(InstallerUserBasename).exe
displayName: "WiX5: Reattach Engines to Bundles"
wix burn reattach installer\$(InstallerFolder)\$(InstallerRelativePath)\$(InstallerBasename).exe -engine installer\engine.exe -o installer\$(InstallerFolder)\$(InstallerRelativePath)\$(InstallerBasename).exe
displayName: "${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} WiX5: Reattach Engine to Bundle"
- pwsh: |-
& wix burn extract -oba installer\ba\m "$(InstallerMachineRoot)\$(InstallerMachineBasename).exe"
& wix burn extract -oba installer\ba\u "$(InstallerUserRoot)\$(InstallerUserBasename).exe"
Get-ChildItem installer\ba -Recurse -Include *.exe,*.dll | Get-AuthenticodeSignature | ForEach-Object {
If ($_.Status -Ne "Valid") {
Write-Error $_.StatusMessage
} Else {
Write-Host $_.StatusMessage
}
}
& git clean -fdx installer\ba
displayName: "WiX5: Verify Bootstrapper content is signed"
- template: steps-esrp-sign-files-authenticode.yml
- template: steps-esrp-signing.yml
parameters:
displayName: Sign Final Bootstrappers
displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Sign Final Bootstrapper
signingIdentity: ${{ parameters.signingIdentity }}
folder: 'installer'
pattern: '**/PowerToys*Setup-*.exe'
inputs:
FolderPath: 'installer/$(InstallerFolder)/$(InstallerRelativePath)'
signType: batchSigning
batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_installer.json'
ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml'
#### END BOOTSTRAP
## END INSTALLER

View File

@@ -1,45 +0,0 @@
parameters:
- name: displayName
type: string
default: Sign Specific Files
- name: folder
type: string
- name: pattern
type: string
- name: signingIdentity
type: object
default: {}
steps:
- template: steps-esrp-signing.yml
parameters:
displayName: ${{ parameters.displayName }}
signingIdentity: ${{ parameters.signingIdentity }}
inputs:
FolderPath: ${{ parameters.folder }}
Pattern: ${{ parameters.pattern }}
UseMinimatch: true
signConfigType: inlineSignParams
inlineOperation: |-
[
{
"KeyCode": "CP-230012",
"OperationCode": "SigntoolSign",
"Parameters": {
"OpusName": "Microsoft",
"OpusInfo": "http://www.microsoft.com",
"FileDigest": "/fd \"SHA256\"",
"PageHash": "/NPH",
"TimeStamp": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
},
"ToolName": "sign",
"ToolVersion": "1.0"
},
{
"KeyCode": "CP-230012",
"OperationCode": "SigntoolVerify",
"Parameters": {},
"ToolName": "sign",
"ToolVersion": "1.0"
}
]

View File

@@ -1,56 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Hybrid CRT configuration -->
<PropertyGroup Condition="'$(HybridCrtConfiguration)'==''">
<HybridCrtConfiguration>$(Configuration)</HybridCrtConfiguration>
</PropertyGroup>
<!-- Skip Hybrid CRT for AppContainer/UWP projects as they require MultiThreadedDLL -->
<PropertyGroup Condition="'$(AppContainerApplication)'=='true' and '$(_VC_Target_Library_Platform)'!='Desktop'">
<HybridCrtConfiguration></HybridCrtConfiguration>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(HybridCrtConfiguration)'=='Debug'">
<ClCompile>
<!-- We use MultiThreadedDebug, rather than MultiThreadedDebugDLL, to avoid DLL dependencies on VCRUNTIME140d.dll and MSVCP140d.dll. -->
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<!-- Link statically against the runtime and STL, but link dynamically against the CRT by ignoring the static CRT
lib and instead linking against the Universal CRT DLL import library. This "hybrid" linking mechanism is
supported according to the CRT maintainer. Dynamic linking against the CRT makes the binaries a bit smaller
than they would otherwise be if the CRT, runtime, and STL were all statically linked in. -->
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries);libucrtd.lib</IgnoreSpecificDefaultLibraries>
<AdditionalOptions>%(AdditionalOptions) /defaultlib:ucrtd.lib</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(HybridCrtConfiguration)'=='Release'">
<ClCompile>
<!-- We use MultiThreaded, rather than MultiThreadedDLL, to avoid DLL dependencies on VCRUNTIME140.dll and MSVCP140.dll. -->
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<!-- Link statically against the runtime and STL, but link dynamically against the CRT by ignoring the static CRT
lib and instead linking against the Universal CRT DLL import library. This "hybrid" linking mechanism is
supported according to the CRT maintainer. Dynamic linking against the CRT makes the binaries a bit smaller
than they would otherwise be if the CRT, runtime, and STL were all statically linked in. -->
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries);libucrt.lib</IgnoreSpecificDefaultLibraries>
<AdditionalOptions>%(AdditionalOptions) /defaultlib:ucrt.lib</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<!-- AppContainer/UWP projects must use MultiThreadedDLL -->
<ItemDefinitionGroup Condition="'$(AppContainerApplication)'=='true' and '$(_VC_Target_Library_Platform)'!='Desktop' And '$(Configuration)'=='Debug'">
<ClCompile>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(AppContainerApplication)'=='true' and '$(_VC_Target_Library_Platform)'!='Desktop' And '$(Configuration)'=='Release'">
<ClCompile>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
</ClCompile>
</ItemDefinitionGroup>
<!-- Project configurations -->
<ItemGroup Label="ProjectConfigurations">
@@ -123,6 +73,7 @@
<ClCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Optimization>Disabled</Optimization>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
@@ -132,6 +83,7 @@
<ClCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Optimization>MaxSpeed</Optimization>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
</ClCompile>

View File

@@ -34,8 +34,13 @@
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<OutDir>$(Platform)\$(Configuration)\SetupShared\</OutDir>
<IntDir>$(SolutionDir)$(ProjectName)\$(Platform)\$(Configuration)\SetupShared\obj\</IntDir>
<OutDir Condition=" '$(PerUser)' != 'true' ">$(Platform)\$(Configuration)\MachineSetup\</OutDir>
<OutDir Condition=" '$(PerUser)' == 'true' ">$(Platform)\$(Configuration)\UserSetup\</OutDir>
<IntDir Condition=" '$(PerUser)' != 'true' ">$(SolutionDir)$(ProjectName)\$(Platform)\$(Configuration)\MachineSetup\obj\</IntDir>
<IntDir Condition=" '$(PerUser)' == 'true' ">$(SolutionDir)$(ProjectName)\$(Platform)\$(Configuration)\UserSetup\obj\</IntDir>
<!-- The CMD script below checks this value, and it is **CASE SENSITIVE** -->
<NormalizedPerUserValue>false</NormalizedPerUserValue>
<NormalizedPerUserValue Condition=" '$(PerUser)' == 'true' ">true</NormalizedPerUserValue>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<LinkIncremental>true</LinkIncremental>
@@ -75,7 +80,8 @@
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\WinAppSDK.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\WinAppSDK.wxs.bk""""
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\WinUI3Applications.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\WinUI3Applications.wxs.bk""""
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\Workspaces.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\Workspaces.wxs.bk""""
call powershell.exe -NonInteractive -executionpolicy Unrestricted -File ..\PowerToysSetupVNext\generateAllFileComponents.ps1 -platform $(Platform)
if not "$(NormalizedPerUserValue)" == "true" call powershell.exe -NonInteractive -executionpolicy Unrestricted -File ..\PowerToysSetupVNext\generateAllFileComponents.ps1 -platform $(Platform)
if "$(NormalizedPerUserValue)" == "true" call powershell.exe -NonInteractive -executionpolicy Unrestricted -File ..\PowerToysSetupVNext\generateAllFileComponents.ps1 -platform $(Platform) -installscopeperuser $(NormalizedPerUserValue)
</Command>
<Message>Backing up original files and populating .NET and WPF Runtime dependencies for WiX3 based installer</Message>
</PreBuildEvent>
@@ -109,6 +115,7 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;CUSTOMACTIONTEST_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
@@ -121,6 +128,7 @@
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;CUSTOMACTIONTEST_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
</ClCompile>
<Link>
@@ -173,4 +181,4 @@
<Error Condition="!Exists('..\packages\WixToolset.DUtil.5.0.2\build\WixToolset.DUtil.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\WixToolset.DUtil.5.0.2\build\WixToolset.DUtil.props'))" />
<Error Condition="!Exists('..\packages\WixToolset.WcaUtil.5.0.2\build\WixToolset.WcaUtil.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\WixToolset.WcaUtil.5.0.2\build\WixToolset.WcaUtil.props'))" />
</Target>
</Project>
</Project>

View File

@@ -4,13 +4,6 @@
<Fragment>
<DirectoryRef Id="WinUI3AppsInstallFolder">
<Directory Id="CmdPalInstallFolder" Name="CmdPal">
<Directory Id="CmdPalDepsInstallFolder" Name="Dependencies">
<?if $(sys.BUILDARCH) = x64 ?>
<Directory Id="CmdPalDepsX64InstallFolder" Name="x64" />
<?else?>
<Directory Id="CmdPalDepsArm64InstallFolder" Name="arm64" />
<?endif?>
</Directory>
</Directory>
</DirectoryRef>
<DirectoryRef Id="CmdPalInstallFolder" FileSource="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test">
@@ -25,40 +18,14 @@
<?endif?>
</Component>
</DirectoryRef>
<?if $(sys.BUILDARCH) = x64 ?>
<DirectoryRef Id="CmdPalDepsX64InstallFolder" FileSource="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Dependencies\x64">
<Component Id="Module_CmdPal_Deps" Guid="C2790FC4-0665-4462-947A-D942A2AABFF0" Bitness="always64">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="Module_CmdPal_Deps" Value="" KeyPath="yes" />
</RegistryKey>
<File Id="Microsoft.VCLibs.x64.14.00.Desktop.appx" Source="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Dependencies\x64\Microsoft.VCLibs.x64.14.00.Desktop.appx" />
</Component>
</DirectoryRef>
<?else?>
<DirectoryRef Id="CmdPalDepsArm64InstallFolder" FileSource="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Dependencies\arm64">
<Component Id="Module_CmdPal_Deps" Guid="C2790FC4-0665-4462-947A-D942A2AABFF0" Bitness="always64">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="Module_CmdPal_Deps" Value="" KeyPath="yes" />
</RegistryKey>
<File Id="Microsoft.VCLibs.ARM64.14.00.Desktop.appx" Source="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Dependencies\arm64\Microsoft.VCLibs.ARM64.14.00.Desktop.appx" />
</Component>
</DirectoryRef>
<?endif?>
<ComponentGroup Id="CmdPalComponentGroup">
<Component Id="RemoveCmdPalFolder" Guid="2DF90C08-CC75-4245-A14E-B82904636C53" Directory="INSTALLFOLDER">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="RemoveCmdPalFolder" Value="" KeyPath="yes" />
</RegistryKey>
<RemoveFolder Id="RemoveCmdPalInstallDirFolder" Directory="CmdPalInstallFolder" On="uninstall" />
<RemoveFolder Id="RemoveCmdPalDepsInstallDirFolder" Directory="CmdPalDepsInstallFolder" On="uninstall" />
<?if $(sys.BUILDARCH) = x64 ?>
<RemoveFolder Id="RemoveCmdPalDepsX64InstallDirFolder" Directory="CmdPalDepsX64InstallFolder" On="uninstall" />
<?else?>
<RemoveFolder Id="RemoveCmdPalDepsArm64InstallDirFolder" Directory="CmdPalDepsArm64InstallFolder" On="uninstall" />
<?endif?>
</Component>
<ComponentRef Id="Module_CmdPal" />
<ComponentRef Id="Module_CmdPal_Deps" />
</ComponentGroup>
</Fragment>
</Wix>

View File

@@ -1,5 +1,4 @@
<Project>
<Import Project="..\..\Directory.Build.props" Condition="Exists('..\..\Directory.Build.props')" />
<Import Project="..\..\src\Version.props" Condition="Exists('..\..\src\Version.props')" />
<PropertyGroup>
<!-- Set BaseIntermediateOutputPath for each project to avoid conflicts -->
@@ -9,4 +8,4 @@
<!-- Set MSBuildProjectExtensionsPath to use the BaseIntermediateOutputPath -->
<MSBuildProjectExtensionsPath Condition="'$(BaseIntermediateOutputPath)' != ''">$(BaseIntermediateOutputPath)</MSBuildProjectExtensionsPath>
</PropertyGroup>
</Project>
</Project>

View File

@@ -60,12 +60,6 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil
call move /Y ..\..\..\Workspaces.wxs.bk ..\..\..\Workspaces.wxs
</PostBuildEvent>
</PropertyGroup>
<PropertyGroup Condition="'$(RunBuildEvents)'=='false'">
<PostBuildEvent></PostBuildEvent>
<RunPostBuildEvent></RunPostBuildEvent>
<PreBuildEventUseInBuild>false</PreBuildEventUseInBuild>
<PostBuildEventUseInBuild>false</PostBuildEventUseInBuild>
</PropertyGroup>
<PropertyGroup Label="UserMacros" Condition=" '$(PerUser)' == 'true' ">
<DefineConstants>$(DefineConstants);PerUser=true</DefineConstants>
</PropertyGroup>

View File

@@ -1,7 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|ARM64">
<Configuration>Debug</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM64">
<Configuration>Release</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{F8B9F842-F5C3-4A2D-8C85-7F8B9E2B4F1D}</ProjectGuid>
<ConfigurationType>DynamicLibrary</ConfigurationType>
<CharacterSet>Unicode</CharacterSet>
<TargetName>SilentFilesInUseBAFunction</TargetName>
<ProjectName>PowerToysSetupCustomActionsVNext</ProjectName>
<ProjectModuleDefinitionFile>bafunctions.def</ProjectModuleDefinitionFile>
@@ -10,6 +33,7 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<!-- Configuration-specific property groups -->
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
@@ -41,10 +65,7 @@
</PropertyGroup>
<ItemGroup>
<ClCompile Include="SilentFilesInUseBAFunctions.cpp">
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
</ClCompile>
<ClCompile Include="SilentFilesInUseBAFunctions.cpp" />
<ClCompile Include="bafunctions.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
<PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
@@ -71,5 +92,31 @@
</Link>
</ItemDefinitionGroup>
<!-- C++ source compile-specific things for Debug/Release configurations -->
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Optimization>Disabled</Optimization>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Optimization>MaxSpeed</Optimization>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
</Project>

View File

@@ -18,6 +18,7 @@ public: // IBootstrapperApplication
BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** CUSTOM BA FUNCTION SYSTEM ACTIVE *** Running detect begin BA function. fCached=%d, registrationType=%d, cPackages=%u, fCancel=%d", fCached, registrationType, cPackages, *pfCancel);
LExit:
return hr;
}
@@ -31,6 +32,12 @@ public: // IBAFunctions
BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** CUSTOM BA FUNCTION SYSTEM ACTIVE *** Running plan begin BA function. cPackages=%u, fCancel=%d", cPackages, *pfCancel);
//-------------------------------------------------------------------------------------------------
// YOUR CODE GOES HERE
// BalExitOnFailure(hr, "Change this message to represent real error handling.");
//-------------------------------------------------------------------------------------------------
LExit:
return hr;
}
@@ -56,7 +63,6 @@ public: // IBAFunctions
)
{
HRESULT hr = S_OK;
UNREFERENCED_PARAMETER(source);
BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** CUSTOM BA FUNCTION CALLED *** Running OnExecuteFilesInUse BA function. packageId=%ls, cFiles=%u, recommendation=%d", wzPackageId, cFiles, nRecommendation);

View File

@@ -1,7 +1,9 @@
[CmdletBinding()]
Param(
[Parameter(Mandatory = $True, Position = 1)]
[string]$platform
[string]$platform,
[Parameter(Mandatory = $False, Position = 2)]
[string]$installscopeperuser = "false"
)
Function Generate-FileList() {
@@ -75,7 +77,9 @@ Function Generate-FileComponents() {
[Parameter(Mandatory = $True, Position = 1)]
[string]$fileListName,
[Parameter(Mandatory = $True, Position = 2)]
[string]$wxsFilePath
[string]$wxsFilePath,
[Parameter(Mandatory = $True, Position = 3)]
[string]$regroot
)
$wxsFile = Get-Content $wxsFilePath;
@@ -96,7 +100,7 @@ Function Generate-FileComponents() {
$componentDefs +=
@"
<Component Id="$($componentId)" Guid="$((New-Guid).ToString().ToUpper())">
<RegistryKey Root="`$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryKey Root="$($regroot)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="$($componentId)" Value="" KeyPath="yes"/>
</RegistryKey>`r`n
"@
@@ -130,188 +134,194 @@ if ($platform -ceq "arm64") {
$platform = "ARM64"
}
if ($installscopeperuser -eq "true") {
$registryroot = "HKCU"
} else {
$registryroot = "HKLM"
}
#BaseApplications
Generate-FileList -fileDepsJson "" -fileListName BaseApplicationsFiles -wxsFilePath $PSScriptRoot\BaseApplications.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release"
Generate-FileComponents -fileListName "BaseApplicationsFiles" -wxsFilePath $PSScriptRoot\BaseApplications.wxs
Generate-FileComponents -fileListName "BaseApplicationsFiles" -wxsFilePath $PSScriptRoot\BaseApplications.wxs -regroot $registryroot
#WinUI3Applications
Generate-FileList -fileDepsJson "" -fileListName WinUI3ApplicationsFiles -wxsFilePath $PSScriptRoot\WinUI3Applications.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps"
Generate-FileComponents -fileListName "WinUI3ApplicationsFiles" -wxsFilePath $PSScriptRoot\WinUI3Applications.wxs
Generate-FileComponents -fileListName "WinUI3ApplicationsFiles" -wxsFilePath $PSScriptRoot\WinUI3Applications.wxs -regroot $registryroot
#AdvancedPaste
Generate-FileList -fileDepsJson "" -fileListName AdvancedPasteAssetsFiles -wxsFilePath $PSScriptRoot\AdvancedPaste.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\AdvancedPaste"
Generate-FileComponents -fileListName "AdvancedPasteAssetsFiles" -wxsFilePath $PSScriptRoot\AdvancedPaste.wxs
Generate-FileComponents -fileListName "AdvancedPasteAssetsFiles" -wxsFilePath $PSScriptRoot\AdvancedPaste.wxs -regroot $registryroot
#AwakeFiles
Generate-FileList -fileDepsJson "" -fileListName AwakeImagesFiles -wxsFilePath $PSScriptRoot\Awake.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\Assets\Awake"
Generate-FileComponents -fileListName "AwakeImagesFiles" -wxsFilePath $PSScriptRoot\Awake.wxs
Generate-FileComponents -fileListName "AwakeImagesFiles" -wxsFilePath $PSScriptRoot\Awake.wxs -regroot $registryroot
#ColorPicker
Generate-FileList -fileDepsJson "" -fileListName ColorPickerAssetsFiles -wxsFilePath $PSScriptRoot\ColorPicker.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\Assets\ColorPicker"
Generate-FileComponents -fileListName "ColorPickerAssetsFiles" -wxsFilePath $PSScriptRoot\ColorPicker.wxs
Generate-FileComponents -fileListName "ColorPickerAssetsFiles" -wxsFilePath $PSScriptRoot\ColorPicker.wxs -regroot $registryroot
#Environment Variables
Generate-FileList -fileDepsJson "" -fileListName EnvironmentVariablesAssetsFiles -wxsFilePath $PSScriptRoot\EnvironmentVariables.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\EnvironmentVariables"
Generate-FileComponents -fileListName "EnvironmentVariablesAssetsFiles" -wxsFilePath $PSScriptRoot\EnvironmentVariables.wxs
Generate-FileComponents -fileListName "EnvironmentVariablesAssetsFiles" -wxsFilePath $PSScriptRoot\EnvironmentVariables.wxs -regroot $registryroot
#FileExplorerAdd-ons
Generate-FileList -fileDepsJson "" -fileListName MonacoPreviewHandlerMonacoAssetsFiles -wxsFilePath $PSScriptRoot\FileExplorerPreview.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\Assets\Monaco"
Generate-FileList -fileDepsJson "" -fileListName MonacoPreviewHandlerCustomLanguagesFiles -wxsFilePath $PSScriptRoot\FileExplorerPreview.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\Assets\Monaco\customLanguages"
Generate-FileComponents -fileListName "MonacoPreviewHandlerMonacoAssetsFiles" -wxsFilePath $PSScriptRoot\FileExplorerPreview.wxs
Generate-FileComponents -fileListName "MonacoPreviewHandlerCustomLanguagesFiles" -wxsFilePath $PSScriptRoot\FileExplorerPreview.wxs
Generate-FileComponents -fileListName "MonacoPreviewHandlerMonacoAssetsFiles" -wxsFilePath $PSScriptRoot\FileExplorerPreview.wxs -regroot $registryroot
Generate-FileComponents -fileListName "MonacoPreviewHandlerCustomLanguagesFiles" -wxsFilePath $PSScriptRoot\FileExplorerPreview.wxs -regroot $registryroot
#FileLocksmith
Generate-FileList -fileDepsJson "" -fileListName FileLocksmithAssetsFiles -wxsFilePath $PSScriptRoot\FileLocksmith.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\FileLocksmith"
Generate-FileComponents -fileListName "FileLocksmithAssetsFiles" -wxsFilePath $PSScriptRoot\FileLocksmith.wxs
Generate-FileComponents -fileListName "FileLocksmithAssetsFiles" -wxsFilePath $PSScriptRoot\FileLocksmith.wxs -regroot $registryroot
#Hosts
Generate-FileList -fileDepsJson "" -fileListName HostsAssetsFiles -wxsFilePath $PSScriptRoot\Hosts.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\Hosts"
Generate-FileComponents -fileListName "HostsAssetsFiles" -wxsFilePath $PSScriptRoot\Hosts.wxs
Generate-FileComponents -fileListName "HostsAssetsFiles" -wxsFilePath $PSScriptRoot\Hosts.wxs -regroot $registryroot
#ImageResizer
Generate-FileList -fileDepsJson "" -fileListName ImageResizerAssetsFiles -wxsFilePath $PSScriptRoot\ImageResizer.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\ImageResizer"
Generate-FileComponents -fileListName "ImageResizerAssetsFiles" -wxsFilePath $PSScriptRoot\ImageResizer.wxs
Generate-FileComponents -fileListName "ImageResizerAssetsFiles" -wxsFilePath $PSScriptRoot\ImageResizer.wxs -regroot $registryroot
# Light Switch Service
Generate-FileList -fileDepsJson "" -fileListName LightSwitchFiles -wxsFilePath $PSScriptRoot\LightSwitch.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\LightSwitchService"
Generate-FileComponents -fileListName "LightSwitchFiles" -wxsFilePath $PSScriptRoot\LightSwitch.wxs
Generate-FileComponents -fileListName "LightSwitchFiles" -wxsFilePath $PSScriptRoot\LightSwitch.wxs -regroot $registryroot
#New+
Generate-FileList -fileDepsJson "" -fileListName NewPlusAssetsFiles -wxsFilePath $PSScriptRoot\NewPlus.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\NewPlus"
Generate-FileComponents -fileListName "NewPlusAssetsFiles" -wxsFilePath $PSScriptRoot\NewPlus.wxs
Generate-FileComponents -fileListName "NewPlusAssetsFiles" -wxsFilePath $PSScriptRoot\NewPlus.wxs -regroot $registryroot
#Peek
Generate-FileList -fileDepsJson "" -fileListName PeekAssetsFiles -wxsFilePath $PSScriptRoot\Peek.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\Peek\"
Generate-FileComponents -fileListName "PeekAssetsFiles" -wxsFilePath $PSScriptRoot\Peek.wxs
Generate-FileComponents -fileListName "PeekAssetsFiles" -wxsFilePath $PSScriptRoot\Peek.wxs -regroot $registryroot
#PowerRename
Generate-FileList -fileDepsJson "" -fileListName PowerRenameAssetsFiles -wxsFilePath $PSScriptRoot\PowerRename.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\PowerRename\"
Generate-FileComponents -fileListName "PowerRenameAssetsFiles" -wxsFilePath $PSScriptRoot\PowerRename.wxs
Generate-FileComponents -fileListName "PowerRenameAssetsFiles" -wxsFilePath $PSScriptRoot\PowerRename.wxs -regroot $registryroot
#RegistryPreview
Generate-FileList -fileDepsJson "" -fileListName RegistryPreviewAssetsFiles -wxsFilePath $PSScriptRoot\RegistryPreview.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\RegistryPreview\"
Generate-FileComponents -fileListName "RegistryPreviewAssetsFiles" -wxsFilePath $PSScriptRoot\RegistryPreview.wxs
Generate-FileComponents -fileListName "RegistryPreviewAssetsFiles" -wxsFilePath $PSScriptRoot\RegistryPreview.wxs -regroot $registryroot
#Run
Generate-FileList -fileDepsJson "" -fileListName launcherImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\Assets\PowerLauncher"
Generate-FileComponents -fileListName "launcherImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "launcherImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
## Plugins
###Calculator
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Calculator\Microsoft.PowerToys.Run.Plugin.Calculator.deps.json" -fileListName calcComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName calcImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Calculator\Images"
Generate-FileComponents -fileListName "calcComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "calcImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "calcComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "calcImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
###Folder
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Folder\Microsoft.Plugin.Folder.deps.json" -fileListName FolderComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName FolderImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Folder\Images"
Generate-FileComponents -fileListName "FolderComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "FolderImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "FolderComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "FolderImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
###Program
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Program\Microsoft.Plugin.Program.deps.json" -fileListName ProgramComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName ProgramImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Program\Images"
Generate-FileComponents -fileListName "ProgramComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "ProgramImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "ProgramComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "ProgramImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
###Shell
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Shell\Microsoft.Plugin.Shell.deps.json" -fileListName ShellComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName ShellImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Shell\Images"
Generate-FileComponents -fileListName "ShellComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "ShellImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "ShellComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "ShellImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
###Indexer
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Indexer\Microsoft.Plugin.Indexer.deps.json" -fileListName IndexerComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName IndexerImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Indexer\Images"
Generate-FileComponents -fileListName "IndexerComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "IndexerImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "IndexerComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "IndexerImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
###UnitConverter
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\UnitConverter\Community.PowerToys.Run.Plugin.UnitConverter.deps.json" -fileListName UnitConvCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName UnitConvImagesCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\UnitConverter\Images"
Generate-FileComponents -fileListName "UnitConvCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "UnitConvImagesCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "UnitConvCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "UnitConvImagesCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
###WebSearch
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\WebSearch\Community.PowerToys.Run.Plugin.WebSearch.deps.json" -fileListName WebSrchCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName WebSrchImagesCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\WebSearch\Images"
Generate-FileComponents -fileListName "WebSrchCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "WebSrchImagesCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "WebSrchCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "WebSrchImagesCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
###History
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\History\Microsoft.PowerToys.Run.Plugin.History.deps.json" -fileListName HistoryPluginComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName HistoryPluginImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\History\Images"
Generate-FileComponents -fileListName "HistoryPluginComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "HistoryPluginImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "HistoryPluginComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "HistoryPluginImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
###Uri
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Uri\Microsoft.Plugin.Uri.deps.json" -fileListName UriComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName UriImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Uri\Images"
Generate-FileComponents -fileListName "UriComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "UriImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "UriComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "UriImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
###VSCodeWorkspaces
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\VSCodeWorkspaces\Community.PowerToys.Run.Plugin.VSCodeWorkspaces.deps.json" -fileListName VSCWrkCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName VSCWrkImagesCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\VSCodeWorkspaces\Images"
Generate-FileComponents -fileListName "VSCWrkCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "VSCWrkImagesCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "VSCWrkCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "VSCWrkImagesCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
###WindowWalker
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\WindowWalker\Microsoft.Plugin.WindowWalker.deps.json" -fileListName WindowWlkrCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName WindowWlkrImagesCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\WindowWalker\Images"
Generate-FileComponents -fileListName "WindowWlkrCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "WindowWlkrImagesCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "WindowWlkrCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "WindowWlkrImagesCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
###OneNote
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\OneNote\Microsoft.PowerToys.Run.Plugin.OneNote.deps.json" -fileListName OneNoteComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName OneNoteImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\OneNote\Images"
Generate-FileComponents -fileListName "OneNoteComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "OneNoteImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "OneNoteComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "OneNoteImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
###Registry
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Registry\Microsoft.PowerToys.Run.Plugin.Registry.deps.json" -fileListName RegistryComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName RegistryImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Registry\Images"
Generate-FileComponents -fileListName "RegistryComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "RegistryImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "RegistryComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "RegistryImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
###Service
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Service\Microsoft.PowerToys.Run.Plugin.Service.deps.json" -fileListName ServiceComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName ServiceImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Service\Images"
Generate-FileComponents -fileListName "ServiceComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "ServiceImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "ServiceComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "ServiceImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
###System
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\System\Microsoft.PowerToys.Run.Plugin.System.deps.json" -fileListName SystemComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName SystemImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\System\Images"
Generate-FileComponents -fileListName "SystemComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "SystemImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "SystemComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "SystemImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
###TimeDate
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\TimeDate\Microsoft.PowerToys.Run.Plugin.TimeDate.deps.json" -fileListName TimeDateComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName TimeDateImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\TimeDate\Images"
Generate-FileComponents -fileListName "TimeDateComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "TimeDateImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "TimeDateComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "TimeDateImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
###WindowsSettings
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\WindowsSettings\Microsoft.PowerToys.Run.Plugin.WindowsSettings.deps.json" -fileListName WinSetCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName WinSetImagesCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\WindowsSettings\Images"
Generate-FileComponents -fileListName "WinSetCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "WinSetImagesCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "WinSetCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "WinSetImagesCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
###WindowsTerminal
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\WindowsTerminal\Microsoft.PowerToys.Run.Plugin.WindowsTerminal.deps.json" -fileListName WinTermCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName WinTermImagesCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\WindowsTerminal\Images"
Generate-FileComponents -fileListName "WinTermCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "WinTermImagesCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "WinTermCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "WinTermImagesCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
###PowerToys
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\PowerToys\Microsoft.PowerToys.Run.Plugin.PowerToys.deps.json" -fileListName PowerToysCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName PowerToysImagesCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\PowerToys\Images"
Generate-FileComponents -fileListName "PowerToysCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "PowerToysImagesCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "PowerToysCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "PowerToysImagesCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
###ValueGenerator
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\ValueGenerator\Community.PowerToys.Run.Plugin.ValueGenerator.deps.json" -fileListName ValueGeneratorCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName ValueGeneratorImagesCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\ValueGenerator\Images"
Generate-FileComponents -fileListName "ValueGeneratorCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "ValueGeneratorImagesCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "ValueGeneratorCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "ValueGeneratorImagesCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
## Plugins
#ShortcutGuide
Generate-FileList -fileDepsJson "" -fileListName ShortcutGuideSvgFiles -wxsFilePath $PSScriptRoot\ShortcutGuide.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\Assets\ShortcutGuide\"
Generate-FileComponents -fileListName "ShortcutGuideSvgFiles" -wxsFilePath $PSScriptRoot\ShortcutGuide.wxs
Generate-FileComponents -fileListName "ShortcutGuideSvgFiles" -wxsFilePath $PSScriptRoot\ShortcutGuide.wxs -regroot $registryroot
#Settings
Generate-FileList -fileDepsJson "" -fileListName SettingsV2AssetsFiles -wxsFilePath $PSScriptRoot\Settings.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\Settings\"
Generate-FileList -fileDepsJson "" -fileListName SettingsV2AssetsModulesFiles -wxsFilePath $PSScriptRoot\Settings.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\Settings\Modules\"
Generate-FileList -fileDepsJson "" -fileListName SettingsV2OOBEAssetsModulesFiles -wxsFilePath $PSScriptRoot\Settings.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\Settings\Modules\OOBE\"
Generate-FileList -fileDepsJson "" -fileListName SettingsV2OOBEAssetsFluentIconsFiles -wxsFilePath $PSScriptRoot\Settings.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\Settings\Icons\"
Generate-FileComponents -fileListName "SettingsV2AssetsFiles" -wxsFilePath $PSScriptRoot\Settings.wxs
Generate-FileComponents -fileListName "SettingsV2AssetsModulesFiles" -wxsFilePath $PSScriptRoot\Settings.wxs
Generate-FileComponents -fileListName "SettingsV2OOBEAssetsModulesFiles" -wxsFilePath $PSScriptRoot\Settings.wxs
Generate-FileComponents -fileListName "SettingsV2OOBEAssetsFluentIconsFiles" -wxsFilePath $PSScriptRoot\Settings.wxs
Generate-FileComponents -fileListName "SettingsV2AssetsFiles" -wxsFilePath $PSScriptRoot\Settings.wxs -regroot $registryroot
Generate-FileComponents -fileListName "SettingsV2AssetsModulesFiles" -wxsFilePath $PSScriptRoot\Settings.wxs -regroot $registryroot
Generate-FileComponents -fileListName "SettingsV2OOBEAssetsModulesFiles" -wxsFilePath $PSScriptRoot\Settings.wxs -regroot $registryroot
Generate-FileComponents -fileListName "SettingsV2OOBEAssetsFluentIconsFiles" -wxsFilePath $PSScriptRoot\Settings.wxs -regroot $registryroot
#Workspaces
Generate-FileList -fileDepsJson "" -fileListName WorkspacesImagesComponentFiles -wxsFilePath $PSScriptRoot\Workspaces.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\Assets\Workspaces\"
Generate-FileComponents -fileListName "WorkspacesImagesComponentFiles" -wxsFilePath $PSScriptRoot\Workspaces.wxs
Generate-FileComponents -fileListName "WorkspacesImagesComponentFiles" -wxsFilePath $PSScriptRoot\Workspaces.wxs -regroot $registryroot

View File

@@ -36,7 +36,7 @@
</Capabilities>
<Applications>
<Application Id="PowerToys.OCR" Executable="PowerToys.PowerOCR.exe" EntryPoint="Windows.FullTrustApplication">
<Application Id="PowerToys.OCR" Executable="PowerToys.PowerOCR.exe" uap10:TrustLevel="mediumIL" uap10:RuntimeBehavior="win32App">
<uap:VisualElements
DisplayName="PowerToys.OCR"
Description="PowerToys OCR Module"
@@ -45,7 +45,7 @@
Square44x44Logo="Images\Square44x44Logo.png">
</uap:VisualElements>
</Application>
<Application Id="PowerToys.SettingsUI" Executable="WinUI3Apps\PowerToys.Settings.exe" EntryPoint="Windows.FullTrustApplication">
<Application Id="PowerToys.SettingsUI" Executable="PowerToys.Settings.exe" uap10:TrustLevel="mediumIL" uap10:RuntimeBehavior="win32App">
<uap:VisualElements
DisplayName="PowerToys.SettingsUI"
Description="PowerToys Settings UI"
@@ -54,7 +54,7 @@
Square44x44Logo="Images\Square44x44Logo.png">
</uap:VisualElements>
</Application>
<Application Id="PowerToys.ImageResizerUI" Executable="WinUI3Apps\PowerToys.ImageResizer.exe" EntryPoint="Windows.FullTrustApplication">
<Application Id="PowerToys.ImageResizerUI" Executable="WinUI3Apps\PowerToys.ImageResizer.exe" uap10:TrustLevel="mediumIL" uap10:RuntimeBehavior="win32App">
<uap:VisualElements
DisplayName="PowerToys.ImageResizer"
Description="PowerToys Image Resizer UI"

View File

@@ -17,7 +17,7 @@
<CIBuildParam Condition="'$(CIBuild)' != 'true'"></CIBuildParam>
</PropertyGroup>
<Exec Command="pwsh -NonInteractive -ExecutionPolicy Bypass -File &quot;$(MSBuildThisFileDirectory)BuildSparsePackage.ps1&quot; -Platform $(Platform) -Configuration $(Configuration) $(NoSignParam) $(CIBuildParam)"
<Exec Command="powershell -NonInteractive -ExecutionPolicy Bypass -File &quot;$(MSBuildThisFileDirectory)BuildSparsePackage.ps1&quot; -Platform $(Platform) -Configuration $(Configuration) $(NoSignParam) $(CIBuildParam)"
ContinueOnError="false"
WorkingDirectory="$(MSBuildThisFileDirectory)" />
</Target>

View File

@@ -9,6 +9,12 @@
<RootNamespace>CalculatorEngineCommon</RootNamespace>
<AppxPackage>false</AppxPackage>
</PropertyGroup>
<!-- BEGIN common.build.pre.props -->
<PropertyGroup Label="Configuration">
<EnableHybridCRT>true</EnableHybridCRT>
<UseCrtSDKReferenceStaticWarning Condition="'$(EnableHybridCRT)'=='true'">false</UseCrtSDKReferenceStaticWarning>
</PropertyGroup>
<!-- END common.build.pre.props -->
<!-- BEGIN cppwinrt.build.pre.props -->
<PropertyGroup Label="Globals">
<CppWinRTEnabled>true</CppWinRTEnabled>
@@ -19,9 +25,11 @@
</PropertyGroup>
<PropertyGroup>
<MinimalCoreWin>true</MinimalCoreWin>
<AppContainerApplication>false</AppContainerApplication>
<AppContainerApplication>true</AppContainerApplication>
<WindowsStoreApp>true</WindowsStoreApp>
<ApplicationType>Windows Store</ApplicationType>
<UseCrtSDKReference Condition="'$(EnableHybridCRT)'=='true'">false</UseCrtSDKReference>
<!-- The SDK reference breaks the Hybrid CRT -->
</PropertyGroup>
<PropertyGroup>
<!-- We have to use the Desktop platform for Hybrid CRT to work. -->
@@ -140,5 +148,43 @@
</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'))" />
</Target> <!-- END common.build.post.props -->
</Target>
<!-- BEGIN common.build.post.props -->
<!--
The Hybrid CRT model statically links the runtime and STL and dynamically
links the UCRT instead of the VC++ CRT. The UCRT ships with Windows.
WinAppSDK asserts that this is "supported according to the CRT maintainer."
This must come before Microsoft.Cpp.targets because it manipulates ClCompile.RuntimeLibrary.
-->
<ItemDefinitionGroup Condition="'$(EnableHybridCRT)'=='true' and '$(Configuration)'=='Debug'">
<ClCompile>
<!-- We use MultiThreadedDebug, rather than MultiThreadedDebugDLL, to avoid DLL dependencies on VCRUNTIME140d.dll and MSVCP140d.dll. -->
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<LanguageStandard Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<!-- Link statically against the runtime and STL, but link dynamically against the CRT by ignoring the static CRT
lib and instead linking against the Universal CRT DLL import library. This "hybrid" linking mechanism is
supported according to the CRT maintainer. Dynamic linking against the CRT makes the binaries a bit smaller
than they would otherwise be if the CRT, runtime, and STL were all statically linked in. -->
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries);libucrtd.lib</IgnoreSpecificDefaultLibraries>
<AdditionalOptions>%(AdditionalOptions) /defaultlib:ucrtd.lib</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(EnableHybridCRT)'=='true' and ('$(Configuration)'=='Release' or '$(Configuration)'=='AuditMode')">
<ClCompile>
<!-- We use MultiThreaded, rather than MultiThreadedDLL, to avoid DLL dependencies on VCRUNTIME140.dll and MSVCP140.dll. -->
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<!-- Link statically against the runtime and STL, but link dynamically against the CRT by ignoring the static CRT
lib and instead linking against the Universal CRT DLL import library. This "hybrid" linking mechanism is
supported according to the CRT maintainer. Dynamic linking against the CRT makes the binaries a bit smaller
than they would otherwise be if the CRT, runtime, and STL were all statically linked in. -->
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries);libucrt.lib</IgnoreSpecificDefaultLibraries>
<AdditionalOptions>%(AdditionalOptions) /defaultlib:ucrt.lib</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<!-- END common.build.post.props -->
</Project>

View File

@@ -26,16 +26,6 @@ namespace ManagedCommon
private static readonly string Version = Assembly.GetExecutingAssembly().GetCustomAttribute<AssemblyFileVersionAttribute>()?.Version ?? "Unknown";
/// <summary>
/// Gets the path to the log directory for the current version of the app.
/// </summary>
public static string CurrentVersionLogDirectoryPath { get; private set; }
/// <summary>
/// Gets the path to the log directory for the app.
/// </summary>
public static string AppLogDirectoryPath { get; private set; }
/// <summary>
/// Initializes the logger and sets the path for logging.
/// </summary>
@@ -52,9 +42,6 @@ namespace ManagedCommon
Directory.CreateDirectory(versionedPath);
}
AppLogDirectoryPath = basePath;
CurrentVersionLogDirectoryPath = versionedPath;
var logFilePath = Path.Combine(versionedPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".log");
Trace.Listeners.Add(new TextWriterTraceListener(logFilePath));
@@ -143,7 +130,7 @@ namespace ManagedCommon
{
exMessage +=
"Inner exception: " + Environment.NewLine +
ex.InnerException.GetType() + " (" + ex.InnerException.HResult + "): " + ex.InnerException.Message + Environment.NewLine;
ex.InnerException.GetType() + " (" + ex.HResult + "): " + ex.InnerException.Message + Environment.NewLine;
}
exMessage +=

View File

@@ -63,12 +63,14 @@
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<UseDebugLibraries>true</UseDebugLibraries>
<LinkIncremental>true</LinkIncremental>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<LinkIncremental>false</LinkIncremental>

View File

@@ -46,6 +46,16 @@
<PropertyGroup>
<TargetName>notifications</TargetName>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup>
<ClCompile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>

View File

@@ -82,6 +82,8 @@
<ClCompile>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">MultiThreadedDebug</RuntimeLibrary>
<RuntimeLibrary Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
@@ -93,6 +95,8 @@
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">MultiThreaded</RuntimeLibrary>
<RuntimeLibrary Condition="'$(Configuration)|$(Platform)'=='Release|x64'">MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>

View File

@@ -20,7 +20,7 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TitleBar x:Name="titleBar" IsTabStop="False">
<TitleBar x:Name="titleBar">
<!-- This is a workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/10374, once fixed we should just be using IconSource -->
<TitleBar.LeftHeader>
<ImageIcon

View File

@@ -39,6 +39,7 @@
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<UseDebugLibraries>true</UseDebugLibraries>
<LinkIncremental>true</LinkIncremental>
</ClCompile>
@@ -48,6 +49,7 @@
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<LinkIncremental>false</LinkIncremental>

View File

@@ -20,7 +20,7 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TitleBar x:Name="titleBar" IsTabStop="False">
<TitleBar x:Name="titleBar">
<!-- This is a workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/10374, once fixed we should just be using IconSource -->
<TitleBar.LeftHeader>
<ImageIcon

View File

@@ -20,7 +20,7 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TitleBar x:Name="titleBar" IsTabStop="False">
<TitleBar x:Name="titleBar">
<!-- This is a workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/10374, once fixed we should just be using IconSource -->
<TitleBar.LeftHeader>
<ImageIcon

View File

@@ -1,4 +1,4 @@
#include <windows.h>
#include <windows.h>
#include <tchar.h>
#include "ThemeScheduler.h"
#include "ThemeHelper.h"
@@ -11,15 +11,13 @@
#include <logger/logger_settings.h>
#include <logger/logger.h>
#include <utils/logger_helper.h>
#include <LightSwitchServiceObserver.h>
SERVICE_STATUS g_ServiceStatus = {};
SERVICE_STATUS_HANDLE g_StatusHandle = nullptr;
HANDLE g_ServiceStopEvent = nullptr;
extern int g_lastUpdatedDay = -1;
static int g_lastUpdatedDay = -1;
static ScheduleMode prevMode = ScheduleMode::Off;
static std::wstring prevLat, prevLon;
static int prevMinutes = -1;
VOID WINAPI ServiceMain(DWORD argc, LPTSTR* argv);
VOID WINAPI ServiceCtrlHandler(DWORD dwCtrl);
@@ -150,6 +148,7 @@ static void update_sun_times(auto& settings)
std::wstring wmsg(e.what(), e.what() + strlen(e.what()));
Logger::error(L"[LightSwitchService] Exception during sun time update: {}", wmsg);
}
}
DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
@@ -162,18 +161,25 @@ DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
Logger::info(L"[LightSwitchService] Worker thread starting...");
Logger::info(L"[LightSwitchService] Parent PID: {}", parentPid);
// Initialize settings system
LightSwitchSettings::instance().InitFileWatcher();
LightSwitchServiceObserver observer({ SettingId::LightTime,
SettingId::DarkTime,
SettingId::ScheduleMode,
SettingId::Sunrise_Offset,
SettingId::Sunset_Offset });
// Open the manual override event created by the module interface
HANDLE hManualOverride = OpenEventW(SYNCHRONIZE | EVENT_MODIFY_STATE, FALSE, L"POWERTOYS_LIGHTSWITCH_MANUAL_OVERRIDE");
auto applyTheme = [](int nowMinutes, int lightMinutes, int darkMinutes, const auto& settings) {
bool isLightActive = (lightMinutes < darkMinutes) ? (nowMinutes >= lightMinutes && nowMinutes < darkMinutes) : (nowMinutes >= lightMinutes || nowMinutes < darkMinutes);
bool isLightActive = false;
if (lightMinutes < darkMinutes)
{
// Normal case: sunrise < sunset
isLightActive = (nowMinutes >= lightMinutes && nowMinutes < darkMinutes);
}
else
{
// Wraparound case: e.g. light at 21:00, dark at 06:00
isLightActive = (nowMinutes >= lightMinutes || nowMinutes < darkMinutes);
}
bool isSystemCurrentlyLight = GetCurrentSystemTheme();
bool isAppsCurrentlyLight = GetCurrentAppsTheme();
@@ -206,72 +212,85 @@ DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
}
};
// --- Initial settings load ---
LightSwitchSettings::instance().LoadSettings();
auto& settings = LightSwitchSettings::instance().settings();
SYSTEMTIME st;
GetLocalTime(&st);
int nowMinutes = st.wHour * 60 + st.wMinute;
// --- Initial theme application (if schedule enabled) ---
if (settings.scheduleMode != ScheduleMode::Off)
{
applyTheme(nowMinutes,
settings.lightTime + settings.sunrise_offset,
settings.darkTime + settings.sunset_offset,
settings);
Logger::trace(L"[LightSwitchService] Initialized g_lastUpdatedDay = {}", g_lastUpdatedDay);
SYSTEMTIME st;
GetLocalTime(&st);
int nowMinutes = st.wHour * 60 + st.wMinute;
applyTheme(nowMinutes, settings.lightTime + settings.sunrise_offset, settings.darkTime + settings.sunset_offset, settings);
}
else
{
Logger::info(L"[LightSwitchService] Schedule mode is OFF - ticker suspended, waiting for manual action or mode change.");
}
g_lastUpdatedDay = st.wDay;
ULONGLONG lastSettingsReload = 0;
// --- Main loop ---
for (;;)
{
HANDLE waits[2] = { g_ServiceStopEvent, hParent };
DWORD count = hParent ? 2 : 1;
bool skipRest = false;
LightSwitchSettings::instance().LoadSettings();
const auto& settings = LightSwitchSettings::instance().settings();
bool scheduleJustEnabled = (prevMode == ScheduleMode::Off && settings.scheduleMode != ScheduleMode::Off);
prevMode = settings.scheduleMode;
// Check for changes in schedule mode or coordinates
bool modeChangedToSunset = (prevMode != settings.scheduleMode &&
settings.scheduleMode == ScheduleMode::SunsetToSunrise);
bool coordsChanged = (prevLat != settings.latitude || prevLon != settings.longitude);
// ─── Handle "Schedule Off" Mode ─────────────────────────────────────────────
if ((modeChangedToSunset || coordsChanged) && settings.scheduleMode == ScheduleMode::SunsetToSunrise)
{
Logger::info(L"[LightSwitchService] Mode or coordinates changed, recalculating sun times.");
update_sun_times(settings);
SYSTEMTIME st;
GetLocalTime(&st);
g_lastUpdatedDay = st.wDay;
prevMode = settings.scheduleMode;
prevLat = settings.latitude;
prevLon = settings.longitude;
}
// If schedule is off, idle but keep watching settings and manual override
if (settings.scheduleMode == ScheduleMode::Off)
{
Logger::info(L"[LightSwitchService] Schedule mode OFF - suspending scheduler but keeping service alive.");
if (!hManualOverride)
{
hManualOverride = OpenEventW(SYNCHRONIZE | EVENT_MODIFY_STATE, FALSE, L"POWERTOYS_LIGHTSWITCH_MANUAL_OVERRIDE");
}
HANDLE waitsOff[4];
DWORD countOff = 0;
waitsOff[countOff++] = g_ServiceStopEvent;
HANDLE waits[4];
DWORD count = 0;
waits[count++] = g_ServiceStopEvent;
if (hParent)
waitsOff[countOff++] = hParent;
waits[count++] = hParent;
if (hManualOverride)
waitsOff[countOff++] = hManualOverride;
waitsOff[countOff++] = LightSwitchSettings::instance().GetSettingsChangedEvent();
waits[count++] = hManualOverride;
waits[count++] = LightSwitchSettings::instance().GetSettingsChangedEvent();
for (;;)
{
DWORD wait = WaitForMultipleObjects(countOff, waitsOff, FALSE, INFINITE);
DWORD wait = WaitForMultipleObjects(count, waits, FALSE, INFINITE);
if (wait == WAIT_OBJECT_0)
// --- Handle exit signals ---
if (wait == WAIT_OBJECT_0) // stop event
{
Logger::info(L"[LightSwitchService] Stop event triggered - exiting worker loop.");
goto cleanup;
break;
}
if (hParent && wait == WAIT_OBJECT_0 + 1)
{
Logger::info(L"[LightSwitchService] Parent exited - stopping service.");
goto cleanup;
break;
}
// --- Manual override triggered ---
if (wait == WAIT_OBJECT_0 + (hParent ? 2 : 1))
{
Logger::info(L"[LightSwitchService] Manual override received while schedule OFF.");
@@ -279,13 +298,15 @@ DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
continue;
}
// --- Settings file changed ---
if (wait == WAIT_OBJECT_0 + (hParent ? 3 : 2))
{
Logger::trace(L"[LightSwitchService] Settings change event triggered, reloading settings...");
ResetEvent(LightSwitchSettings::instance().GetSettingsChangedEvent());
LightSwitchSettings::instance().LoadSettings();
const auto& newSettings = LightSwitchSettings::instance().settings();
lastSettingsReload = GetTickCount64();
if (newSettings.scheduleMode != ScheduleMode::Off)
{
@@ -294,170 +315,68 @@ DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
}
}
}
continue;
}
// ─── Normal Schedule Loop ───────────────────────────────────────────────────
ULONGLONG nowTick = GetTickCount64();
bool recentSettingsReload = (nowTick - lastSettingsReload < 5000);
if (g_lastUpdatedDay != -1)
// --- When schedule is active, run once per minute ---
SYSTEMTIME st;
GetLocalTime(&st);
int nowMinutes = st.wHour * 60 + st.wMinute;
// Refresh suntimes at day boundary
if ((g_lastUpdatedDay != st.wDay) && (settings.scheduleMode == ScheduleMode::SunsetToSunrise))
{
bool manualOverrideActive = (hManualOverride && WaitForSingleObject(hManualOverride, 0) == WAIT_OBJECT_0);
update_sun_times(settings);
g_lastUpdatedDay = st.wDay;
Logger::info(L"[LightSwitchService] Recalculated sun times at new day boundary.");
}
if (settings.scheduleMode != ScheduleMode::Off && !recentSettingsReload && !scheduleJustEnabled)
// Have to do this again in case settings got updated in the refresh suntimes chunk
LightSwitchSettings::instance().LoadSettings();
const auto& currentSettings = LightSwitchSettings::instance().settings();
wchar_t msg[160];
swprintf_s(msg,
L"[LightSwitchService] now=%02d:%02d | light=%02d:%02d | dark=%02d:%02d | mode=%d",
st.wHour,
st.wMinute,
currentSettings.lightTime / 60,
currentSettings.lightTime % 60,
currentSettings.darkTime / 60,
currentSettings.darkTime % 60,
static_cast<int>(currentSettings.scheduleMode));
Logger::info(msg);
// --- Manual override check ---
bool manualOverrideActive = false;
if (hManualOverride)
{
manualOverrideActive = (WaitForSingleObject(hManualOverride, 0) == WAIT_OBJECT_0);
}
if (manualOverrideActive)
{
if (nowMinutes == (currentSettings.lightTime + currentSettings.sunrise_offset) % 1440 ||
nowMinutes == (currentSettings.darkTime + currentSettings.sunset_offset) % 1440)
{
Logger::debug(L"[LightSwitchService] Checking if manual override is active...");
bool manualOverrideActive = (hManualOverride && WaitForSingleObject(hManualOverride, 0) == WAIT_OBJECT_0);
Logger::debug(L"[LightSwitchService] Manual override active = {}", manualOverrideActive);
if (!manualOverrideActive)
{
bool currentSystemTheme = GetCurrentSystemTheme();
bool currentAppsTheme = GetCurrentAppsTheme();
SYSTEMTIME st;
GetLocalTime(&st);
int nowMinutes = st.wHour * 60 + st.wMinute;
bool shouldBeLight = (settings.lightTime < settings.darkTime) ? (nowMinutes >= settings.lightTime && nowMinutes < settings.darkTime) : (nowMinutes >= settings.lightTime || nowMinutes < settings.darkTime);
Logger::debug(L"[LightSwitchService] shouldBeLight = {}", shouldBeLight);
if ((settings.changeSystem && (currentSystemTheme != shouldBeLight)) ||
(settings.changeApps && (currentAppsTheme != shouldBeLight)))
{
Logger::debug(L"[LightSwitchService] External theme change detected - enabling manual override");
if (!hManualOverride)
{
hManualOverride = OpenEventW(SYNCHRONIZE | EVENT_MODIFY_STATE, FALSE, L"POWERTOYS_LIGHTSWITCH_MANUAL_OVERRIDE");
if (!hManualOverride)
hManualOverride = CreateEventW(nullptr, TRUE, FALSE, L"POWERTOYS_LIGHTSWITCH_MANUAL_OVERRIDE");
}
if (hManualOverride)
{
SetEvent(hManualOverride);
Logger::info(L"[LightSwitchService] Detected manual theme change outside of LightSwitch. Triggering manual override.");
skipRest = true;
}
}
}
ResetEvent(hManualOverride);
Logger::info(L"[LightSwitchService] Manual override cleared at boundary");
}
else
{
Logger::debug(L"[LightSwitchService] Skipping external-change detection (schedule off, recent reload, or just enabled).");
Logger::info(L"[LightSwitchService] Skipping schedule due to manual override");
goto sleep_until_next_minute;
}
}
// ─── Apply Schedule Logic ───────────────────────────────────────────────────
if (!skipRest)
{
bool modeChangedToSunset = (prevMode != settings.scheduleMode &&
settings.scheduleMode == ScheduleMode::SunsetToSunrise);
bool coordsChanged = (prevLat != settings.latitude || prevLon != settings.longitude);
applyTheme(nowMinutes, currentSettings.lightTime + currentSettings.sunrise_offset, currentSettings.darkTime + currentSettings.sunset_offset, currentSettings);
if ((modeChangedToSunset || coordsChanged) && settings.scheduleMode == ScheduleMode::SunsetToSunrise)
{
Logger::info(L"[LightSwitchService] Mode or coordinates changed, recalculating sun times.");
update_sun_times(settings);
SYSTEMTIME st;
GetLocalTime(&st);
g_lastUpdatedDay = st.wDay;
prevMode = settings.scheduleMode;
prevLat = settings.latitude;
prevLon = settings.longitude;
}
SYSTEMTIME st;
GetLocalTime(&st);
int nowMinutes = st.wHour * 60 + st.wMinute;
if ((g_lastUpdatedDay != st.wDay) && (settings.scheduleMode == ScheduleMode::SunsetToSunrise))
{
update_sun_times(settings);
g_lastUpdatedDay = st.wDay;
prevMinutes = -1;
Logger::info(L"[LightSwitchService] Recalculated sun times at new day boundary.");
}
LightSwitchSettings::instance().LoadSettings();
const auto& currentSettings = LightSwitchSettings::instance().settings();
wchar_t msg[160];
swprintf_s(msg,
L"[LightSwitchService] now=%02d:%02d | light=%02d:%02d | dark=%02d:%02d | mode=%d",
st.wHour,
st.wMinute,
currentSettings.lightTime / 60,
currentSettings.lightTime % 60,
currentSettings.darkTime / 60,
currentSettings.darkTime % 60,
static_cast<int>(currentSettings.scheduleMode));
Logger::info(msg);
bool manualOverrideActive = false;
if (hManualOverride)
manualOverrideActive = (WaitForSingleObject(hManualOverride, 0) == WAIT_OBJECT_0);
if (manualOverrideActive)
{
int lightBoundary = (currentSettings.lightTime + currentSettings.sunrise_offset) % 1440;
int darkBoundary = (currentSettings.darkTime + currentSettings.sunset_offset) % 1440;
bool crossedLight = false;
bool crossedDark = false;
if (prevMinutes != -1)
{
if (nowMinutes < prevMinutes)
{
crossedLight = (prevMinutes <= lightBoundary || nowMinutes >= lightBoundary);
crossedDark = (prevMinutes <= darkBoundary || nowMinutes >= darkBoundary);
}
else
{
crossedLight = (prevMinutes < lightBoundary && nowMinutes >= lightBoundary);
crossedDark = (prevMinutes < darkBoundary && nowMinutes >= darkBoundary);
}
}
Logger::debug(L"[LightSwitchService] prevMinutes={} nowMinutes={} light={} dark={}",
prevMinutes,
nowMinutes,
lightBoundary,
darkBoundary);
if (crossedLight || crossedDark)
{
ResetEvent(hManualOverride);
Logger::info(L"[LightSwitchService] Manual override cleared after crossing schedule boundary.");
}
else
{
Logger::info(L"[LightSwitchService] Skipping schedule due to manual override");
skipRest = true;
}
}
if (!skipRest)
applyTheme(nowMinutes,
currentSettings.lightTime + currentSettings.sunrise_offset,
currentSettings.darkTime + currentSettings.sunset_offset,
currentSettings);
}
// ─── Wait For Next Minute Tick Or Stop Event ────────────────────────────────
SYSTEMTIME st;
sleep_until_next_minute:
GetLocalTime(&st);
int msToNextMinute = (60 - st.wSecond) * 1000 - st.wMilliseconds;
if (msToNextMinute < 50)
msToNextMinute = 50;
prevMinutes = nowMinutes;
DWORD wait = WaitForMultipleObjects(count, waits, FALSE, msToNextMinute);
if (wait == WAIT_OBJECT_0)
{
@@ -471,7 +390,6 @@ DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
}
}
cleanup:
if (hManualOverride)
CloseHandle(hManualOverride);
if (hParent)
@@ -480,53 +398,6 @@ cleanup:
return 0;
}
void ApplyThemeNow()
{
LightSwitchSettings::instance().LoadSettings();
const auto& settings = LightSwitchSettings::instance().settings();
SYSTEMTIME st;
GetLocalTime(&st);
int nowMinutes = st.wHour * 60 + st.wMinute;
bool shouldBeLight = false;
if (settings.lightTime < settings.darkTime)
shouldBeLight = (nowMinutes >= settings.lightTime && nowMinutes < settings.darkTime);
else
shouldBeLight = (nowMinutes >= settings.lightTime || nowMinutes < settings.darkTime);
bool isSystemCurrentlyLight = GetCurrentSystemTheme();
bool isAppsCurrentlyLight = GetCurrentAppsTheme();
Logger::info(L"[LightSwitchService] Applying (if needed) theme immediately due to schedule change.");
if (shouldBeLight)
{
if (settings.changeSystem && !isSystemCurrentlyLight)
{
SetSystemTheme(true);
Logger::info(L"[LightSwitchService] Changing system theme to light mode.");
}
if (settings.changeApps && !isAppsCurrentlyLight)
{
SetAppsTheme(true);
Logger::info(L"[LightSwitchService] Changing apps theme to light mode.");
}
}
else
{
if (settings.changeSystem && isSystemCurrentlyLight)
{
SetSystemTheme(false);
Logger::info(L"[LightSwitchService] Changing system theme to dark mode.");
}
if (settings.changeApps && isAppsCurrentlyLight)
{
SetAppsTheme(false);
Logger::info(L"[LightSwitchService] Changing apps theme to dark mode.");
}
}
}
int APIENTRY wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{

View File

@@ -74,7 +74,6 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="LightSwitchService.cpp" />
<ClCompile Include="LightSwitchServiceObserver.cpp" />
<ClCompile Include="LightSwitchSettings.cpp" />
<ClCompile Include="SettingsConstants.cpp" />
<ClCompile Include="ThemeHelper.cpp" />
@@ -85,7 +84,6 @@
<ResourceCompile Include="LightSwitchService.rc" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="LightSwitchServiceObserver.h" />
<ClInclude Include="LightSwitchSettings.h" />
<ClInclude Include="SettingsConstants.h" />
<ClInclude Include="SettingsObserver.h" />

View File

@@ -33,9 +33,6 @@
<ClCompile Include="WinHookEventIDs.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="LightSwitchServiceObserver.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ThemeScheduler.h">
@@ -56,9 +53,6 @@
<ClInclude Include="WinHookEventIDs.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="LightSwitchServiceObserver.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />

View File

@@ -1,29 +0,0 @@
#include "LightSwitchServiceObserver.h"
#include <logger.h>
#include "LightSwitchSettings.h"
// These are defined elsewhere in your service module (ServiceWorkerThread.cpp)
extern int g_lastUpdatedDay;
void ApplyThemeNow();
void LightSwitchServiceObserver::SettingsUpdate(SettingId id)
{
Logger::info(L"[LightSwitchService] Setting changed: {}", static_cast<int>(id));
g_lastUpdatedDay = -1;
ApplyThemeNow();
}
bool LightSwitchServiceObserver::WantsToBeNotified(SettingId id) const noexcept
{
switch (id)
{
case SettingId::LightTime:
case SettingId::DarkTime:
case SettingId::ScheduleMode:
case SettingId::Sunrise_Offset:
case SettingId::Sunset_Offset:
return true;
default:
return false;
}
}

View File

@@ -1,16 +0,0 @@
#pragma once
#include "SettingsObserver.h"
// The LightSwitchServiceObserver reacts when LightSwitchSettings changes.
class LightSwitchServiceObserver : public SettingsObserver
{
public:
explicit LightSwitchServiceObserver(std::unordered_set<SettingId> observedSettings) :
SettingsObserver(std::move(observedSettings))
{
}
void SettingsUpdate(SettingId id) override;
bool WantsToBeNotified(SettingId id) const noexcept override;
};

View File

@@ -39,7 +39,6 @@ void LightSwitchSettings::InitFileWatcher()
GetSettingsFileName(),
[this]() {
Logger::info(L"[LightSwitchSettings] Settings file changed, signaling event.");
LoadSettings();
SetEvent(m_settingsChangedEvent);
});
}
@@ -66,11 +65,6 @@ void LightSwitchSettings::NotifyObservers(SettingId id) const
}
}
HANDLE LightSwitchSettings::GetSettingsChangedEvent() const
{
return m_settingsChangedEvent;
}
void LightSwitchSettings::LoadSettings()
{
try

View File

@@ -79,7 +79,7 @@ public:
void LoadSettings();
HANDLE GetSettingsChangedEvent() const;
HANDLE GetSettingsChangedEvent() const { return m_settingsChangedEvent; }
private:
LightSwitchSettings();

View File

@@ -2,7 +2,6 @@
#include <unordered_set>
#include "SettingsConstants.h"
#include "LightSwitchSettings.h"
class LightSwitchSettings;
@@ -23,7 +22,7 @@ public:
// Override this in your class to respond to updates
virtual void SettingsUpdate(SettingId type) {}
virtual bool WantsToBeNotified(SettingId type) const noexcept
bool WantsToBeNotified(SettingId type) const noexcept
{
return m_observedSettings.contains(type);
}

View File

@@ -189,7 +189,7 @@ bool SuperSonar<D>::Initialize(HINSTANCE hinst)
return false;
}
DWORD exStyle = WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_TOOLWINDOW | Shim()->GetExtendedStyle();
DWORD exStyle = WS_EX_TOOLWINDOW | Shim()->GetExtendedStyle();
HWND created = CreateWindowExW(exStyle, className, windowTitle, WS_POPUP, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, m_hwndOwner, nullptr, hinst, this);
if (!created)
{
@@ -269,6 +269,10 @@ LRESULT SuperSonar<D>::BaseWndProc(UINT message, WPARAM wParam, LPARAM lParam) n
case WM_NCHITTEST:
return HTTRANSPARENT;
case WM_SETCURSOR:
SetCursor(LoadCursor(nullptr, IDC_ARROW));
return TRUE;
}
if (message == WM_PRIV_SHORTCUT)
@@ -535,7 +539,7 @@ void SuperSonar<D>::StartSonar()
Trace::MousePointerFocused();
// Cover the entire virtual screen.
// HACK: Draw with 1 pixel off. Otherwise, Windows glitches the task bar transparency when a transparent window fill the whole screen.
SetWindowPos(m_hwnd, HWND_TOPMOST, GetSystemMetrics(SM_XVIRTUALSCREEN) + 1, GetSystemMetrics(SM_YVIRTUALSCREEN) + 1, GetSystemMetrics(SM_CXVIRTUALSCREEN) - 2, GetSystemMetrics(SM_CYVIRTUALSCREEN) - 2, 0);
SetWindowPos(m_hwnd, HWND_TOPMOST, GetSystemMetrics(SM_XVIRTUALSCREEN) + 1, GetSystemMetrics(SM_YVIRTUALSCREEN) + 1, GetSystemMetrics(SM_CXVIRTUALSCREEN) - 2, GetSystemMetrics(SM_CYVIRTUALSCREEN) - 2, SWP_NOACTIVATE);
m_sonarPos = ptNowhere;
OnMouseTimer();
UpdateMouseSnooping();

View File

@@ -64,6 +64,7 @@
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<LanguageStandard>stdcpplatest</LanguageStandard>
</ClCompile>
<Link>
@@ -81,6 +82,7 @@
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<LanguageStandard>stdcpplatest</LanguageStandard>
</ClCompile>
<Link>

View File

@@ -48,6 +48,7 @@
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<LanguageStandard>stdcpplatest</LanguageStandard>
</ClCompile>
<Link>
@@ -65,6 +66,7 @@
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<LanguageStandard>stdcpplatest</LanguageStandard>
</ClCompile>
<Link>

View File

@@ -48,6 +48,7 @@
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<LanguageStandard>stdcpplatest</LanguageStandard>
</ClCompile>
<Link>
@@ -65,6 +66,7 @@
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<LanguageStandard>stdcpplatest</LanguageStandard>
</ClCompile>
<Link>

View File

@@ -49,6 +49,7 @@
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<LanguageStandard>stdcpplatest</LanguageStandard>
</ClCompile>
<Link>
@@ -66,6 +67,7 @@
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<LanguageStandard>stdcpplatest</LanguageStandard>
</ClCompile>
<Link>

View File

@@ -92,7 +92,6 @@
<ClInclude Include="..\NewShellExtensionContextMenu\template_folder.h" />
<ClInclude Include="..\NewShellExtensionContextMenu\template_item.h" />
<ClInclude Include="..\NewShellExtensionContextMenu\trace.h" />
<ClInclude Include="..\NewShellExtensionContextMenu\Helpers.h" />
<ClInclude Include="dll_main.h" />
<ClInclude Include="Generated Files\resource.h" />
<ClInclude Include="pch.h" />
@@ -100,7 +99,7 @@
<ClInclude Include="shell_context_menu_win10.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\NewShellExtensionContextMenu\Helpers.cpp" />
<ClCompile Include="..\..\powerrename\lib\Helpers.cpp" />
<ClCompile Include="..\NewShellExtensionContextMenu\new_utilities.cpp" />
<ClCompile Include="..\NewShellExtensionContextMenu\powertoys_module.cpp" />
<ClCompile Include="..\NewShellExtensionContextMenu\settings.cpp" />

View File

@@ -3,7 +3,6 @@
#pragma once
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#define NOMCX
#define NOHELP
#define NOCOMM

View File

@@ -1,118 +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.
#include "pch.h"
#include "Helpers.h"
#include <regex>
// Minimal subset of PowerRename Helpers used by NewPlus
// This is a copy from PowerRename main branch to avoid cross-module dependencies
HRESULT GetDatedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source, SYSTEMTIME fileTime)
{
std::locale::global(std::locale(""));
HRESULT hr = E_INVALIDARG;
if (source && wcslen(source) > 0)
{
std::wstring res(source);
wchar_t replaceTerm[MAX_PATH] = { 0 };
wchar_t formattedDate[MAX_PATH] = { 0 };
wchar_t localeName[LOCALE_NAME_MAX_LENGTH];
if (GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH) == 0)
{
StringCchCopy(localeName, LOCALE_NAME_MAX_LENGTH, L"en_US");
}
int hour12 = (fileTime.wHour % 12);
if (hour12 == 0)
{
hour12 = 12;
}
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"), replaceTerm);
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%d"), L"$01", (fileTime.wYear % 10));
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$Y"), replaceTerm);
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);
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$MMMM"), replaceTerm);
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"), replaceTerm);
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", fileTime.wMonth);
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"), replaceTerm);
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);
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$DDDD"), replaceTerm);
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"), replaceTerm);
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", fileTime.wDay);
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"), replaceTerm);
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", hour12);
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"), 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);
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);
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", fileTime.wHour);
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"), replaceTerm);
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", fileTime.wMinute);
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"), replaceTerm);
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", fileTime.wSecond);
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"), replaceTerm);
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%03d"), L"$01", fileTime.wMilliseconds);
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"), replaceTerm);
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%d"), L"$01", fileTime.wMilliseconds / 100);
res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$f"), replaceTerm);
hr = StringCchCopy(result, cchMax, res.c_str());
}
return hr;
}

View File

@@ -1,9 +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.
#pragma once
// Minimal subset of PowerRename Helpers used by NewPlus
// This is a copy from PowerRename's main branch to avoid cross-module dependencies
HRESULT GetDatedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source, SYSTEMTIME fileTime);

View File

@@ -67,6 +67,8 @@
<EnableUAC>false</EnableUAC>
<ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
<AdditionalDependencies>runtimeobject.lib;$(CoreLibraryDependencies)</AdditionalDependencies>
<IgnoreSpecificDefaultLibraries>
</IgnoreSpecificDefaultLibraries>
</Link>
<PreBuildEvent>
<Command>del $(OutDir)\NewPlusPackage.msix /q
@@ -98,6 +100,8 @@ MakeAppx.exe pack /d . /p $(OutDir)NewPlusPackage.msix /nv</Command>
<EnableUAC>false</EnableUAC>
<ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
<AdditionalDependencies>runtimeobject.lib;$(CoreLibraryDependencies)</AdditionalDependencies>
<IgnoreSpecificDefaultLibraries>
</IgnoreSpecificDefaultLibraries>
</Link>
<PreBuildEvent>
<Command>del $(OutDir)\NewPlusPackage.msix /q
@@ -110,7 +114,6 @@ MakeAppx.exe pack /d . /p $(OutDir)NewPlusPackage.msix /nv</Command>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="dll_main.h" />
<ClInclude Include="Helpers.h" />
<ClInclude Include="helpers_filesystem.h" />
<ClInclude Include="helpers_variables.h" />
<ClInclude Include="shell_context_menu.h" />
@@ -128,7 +131,7 @@ MakeAppx.exe pack /d . /p $(OutDir)NewPlusPackage.msix /nv</Command>
<ClInclude Include="template_item.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Helpers.cpp" />
<ClCompile Include="..\..\powerrename\lib\Helpers.cpp" />
<ClCompile Include="new_utilities.cpp" />
<ClCompile Include="shell_context_menu.cpp" />
<ClCompile Include="shell_context_sub_menu.cpp" />

View File

@@ -1,7 +1,7 @@
#pragma once
#include <regex>
#include "Helpers.h"
#include "..\..\powerrename\lib\Helpers.h"
#include "helpers_filesystem.h"
#pragma comment(lib, "Pathcch.lib")

View File

@@ -302,9 +302,9 @@ namespace newplus::utilities
POINT mouse_position;
GetCursorPos(&mouse_position);
mouse_position.x -= GetSystemMetrics(SM_CXMENUSIZE);
mouse_position.x = (std::max)(mouse_position.x, 20L);
mouse_position.x = max(mouse_position.x, 20);
mouse_position.y -= GetSystemMetrics(SM_CXMENUSIZE)/2;
mouse_position.y = (std::max)(mouse_position.y, 20L);
mouse_position.y = max(mouse_position.y, 20);
POINT position[] = { mouse_position };
folder_view->SelectAndPositionItems(1, shell_item_to_select_and_position, position, common_select_flags | SVSI_POSITIONITEM);
}

View File

@@ -3,7 +3,6 @@
#pragma once
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#define NOMCX
#define NOHELP
#define NOCOMM
@@ -14,7 +13,6 @@
#include <shellapi.h>
#include <Windows.h>
#include <shlobj.h>
#include <algorithm>
#include <vector>
#include <system_error>
#include <memory>

View File

@@ -60,8 +60,8 @@ std::wstring template_item::get_target_filename(const bool include_starting_digi
std::wstring template_item::remove_starting_digits_from_filename(std::wstring filename) const
{
filename.erase(0, std::min(filename.find_first_not_of(L"0123456789"), filename.size()));
filename.erase(0, std::min(filename.find_first_not_of(L" ."), filename.size()));
filename.erase(0, min(filename.find_first_not_of(L"0123456789"), filename.size()));
filename.erase(0, min(filename.find_first_not_of(L" ."), filename.size()));
return filename;
}

View File

@@ -2,10 +2,8 @@
// 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 Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium.Interactions;
using static Microsoft.PowerToys.UITest.UITestBase;
namespace PowerOCR.UITests;
@@ -21,274 +19,41 @@ public class PowerOCRTests : UITestBase
[TestInitialize]
public void TestInitialize()
{
if (FindAll<NavigationViewItem>(By.AccessibilityId("TextExtractorNavItem")).Count == 0)
if (FindAll<NavigationViewItem>("Text Extractor").Count == 0)
{
// Expand System Tools list-group if needed
Find<NavigationViewItem>(By.AccessibilityId("SystemToolsNavItem")).Click();
// Expand Advanced list-group if needed
Find<NavigationViewItem>("System Tools").Click();
}
Find<NavigationViewItem>(By.AccessibilityId("TextExtractorNavItem")).Click();
Find<NavigationViewItem>("Text Extractor").Click();
Find<ToggleSwitch>(By.AccessibilityId("EnableTextExtractorToggleSwitch")).Toggle(true);
Find<ToggleSwitch>("Enable Text Extractor").Toggle(true);
// Reset activation shortcut to default (Win+Shift+T)
var shortcutControl = Find<Element>(By.AccessibilityId("TextExtractorActivationShortcut"), 5000);
if (shortcutControl != null)
{
shortcutControl.Click();
Thread.Sleep(500);
// Set default shortcut Win+Shift+T
SendKeys(Key.Win, Key.Shift, Key.T);
Thread.Sleep(1000);
// Click Save to confirm
var saveButton = Find<Button>(By.Name("Save"), 3000);
if (saveButton != null)
{
saveButton.Click();
Thread.Sleep(1000);
}
}
SendKeys(Key.Win, Key.D);
}
[TestMethod("PowerOCR.DetectTextExtractor")]
[TestCategory("PowerOCR Detection")]
public void DetectTextExtractorTest()
{
// Step 1: Press the activation shortcut and verify the overlay appears
SendKeys(Key.Win, Key.Shift, Key.T);
var textExtractorWindow = Find<Element>(By.AccessibilityId("TextExtractorWindow"), 10000, true);
Assert.IsNotNull(textExtractorWindow, "TextExtractor window should be found after hotkey activation");
// Step 2: Press Escape and verify the overlay disappears
SendKeys(Key.Esc);
Thread.Sleep(3000);
var windowsAfterEscape = FindAll<Element>(By.AccessibilityId("TextExtractorWindow"), 2000, true);
Assert.AreEqual(0, windowsAfterEscape.Count, "TextExtractor window should be dismissed after pressing Escape");
// Step 3: Press the activation shortcut again and verify the overlay appears
SendKeys(Key.Win, Key.Shift, Key.T);
textExtractorWindow = Find<Element>(By.AccessibilityId("TextExtractorWindow"), 10000, true);
Assert.IsNotNull(textExtractorWindow, "TextExtractor window should appear again after hotkey activation");
// Step 4: Right-click and select Cancel. Verify the overlay disappears
textExtractorWindow.Click(rightClick: true);
Thread.Sleep(500);
// Look for Cancel menu item using its AutomationId
var cancelMenuItem = Find<Element>(By.AccessibilityId("CancelMenuItem"), 3000, true);
Assert.IsNotNull(cancelMenuItem, "Cancel menu item should be available in context menu");
cancelMenuItem.Click();
Thread.Sleep(3000);
var windowsAfterCancel = FindAll<Element>(By.AccessibilityId("TextExtractorWindow"), 2000, true);
Assert.AreEqual(0, windowsAfterCancel.Count, "TextExtractor window should be dismissed after clicking Cancel");
}
[TestMethod("PowerOCR.DisableTextExtractorTest")]
[TestCategory("PowerOCR Settings")]
public void DisableTextExtractorTest()
{
Find<ToggleSwitch>(By.AccessibilityId("EnableTextExtractorToggleSwitch")).Toggle(false);
SendKeys(Key.Win, Key.Shift, Key.T);
// Verify that no TextExtractor window appears
var windowsWhenDisabled = FindAll<Element>(By.AccessibilityId("TextExtractorWindow"), 10000, true);
Assert.AreEqual(0, windowsWhenDisabled.Count, "TextExtractor window should not appear when the utility is disabled");
}
[TestMethod("PowerOCR.ActivationShortcutSettingsTest")]
[TestCategory("PowerOCR Settings")]
public void ActivationShortcutSettingsTest()
{
// Find the activation shortcut control
var shortcutControl = Find<Element>(By.AccessibilityId("TextExtractorActivationShortcut"), 5000);
Assert.IsNotNull(shortcutControl, "Activation shortcut control should be found");
// Click to focus the shortcut control
shortcutControl.Click();
Thread.Sleep(500);
// Test changing the shortcut to Ctrl+Shift+T
SendKeys(Key.Ctrl, Key.Shift, Key.T);
Thread.Sleep(1000);
// Click the Save button to confirm the shortcut change
var saveButton = Find<Button>(By.Name("Save"), 3000);
Assert.IsNotNull(saveButton, "Save button should be found in the shortcut dialog");
saveButton.Click();
Thread.Sleep(1000);
// Test the new shortcut
SendKeys(Key.Ctrl, Key.Shift, Key.T);
Thread.Sleep(3000);
var textExtractorWindow = FindAll<Element>(By.AccessibilityId("TextExtractorWindow"), 3000, true);
Assert.IsTrue(textExtractorWindow.Count > 0, "TextExtractor should activate with new shortcut Ctrl+Shift+T");
}
[TestMethod("PowerOCR.OCRLanguageSettingsTest")]
[TestCategory("PowerOCR Settings")]
public void OCRLanguageSettingsTest()
{
// Find the language combo box
var languageComboBox = Find<ComboBox>(By.AccessibilityId("TextExtractorLanguageComboBox"), 5000);
Assert.IsNotNull(languageComboBox, "Language combo box should be found");
// Click to open the dropdown
languageComboBox.Click();
// Verify dropdown is opened by checking if dropdown items are available
var dropdownItems = FindAll<Element>(By.ClassName("ComboBoxItem"), 2000);
Assert.IsTrue(dropdownItems.Count > 0, "Dropdown should contain language options");
// Close dropdown by pressing Escape
SendKeys(Key.Esc);
}
[TestMethod("PowerOCR.OCRLanguageSelectionTest")]
[TestCategory("PowerOCR Language")]
public void OCRLanguageSelectionTest()
{
// Activate Text Extractor overlay
SendKeys(Key.Win, Key.Shift, Key.T);
Thread.Sleep(3000);
var textExtractorWindow = Find<Element>(By.AccessibilityId("TextExtractorWindow"), 10000, true);
Assert.IsNotNull(textExtractorWindow, "TextExtractor window should be found after hotkey activation");
// Right-click on the canvas to open context menu
textExtractorWindow.Click(rightClick: true);
// Look for language options that should appear after Cancel menu item
var allMenuItems = FindAll<Element>(By.ClassName("MenuItem"), 2000, true);
if (allMenuItems.Count > 4)
try
{
// Find the Cancel menu item first
Element? cancelItem = null;
int cancelIndex = -1;
for (int i = 0; i < allMenuItems.Count; i++)
{
if (allMenuItems[i].GetAttribute("AutomationId") == "CancelMenuItem")
{
cancelItem = allMenuItems[i];
cancelIndex = i;
break;
}
}
SendKeys(Key.Win, Key.Shift, Key.T);
Assert.IsNotNull(cancelItem, "Cancel menu item should be found");
Thread.Sleep(5000);
// Look for language options after Cancel menu item
if (cancelIndex >= 0 && cancelIndex < allMenuItems.Count - 1)
{
// Select the first language option after Cancel
var languageOption = allMenuItems[cancelIndex + 1];
languageOption.Click();
Thread.Sleep(1000);
var textExtractorWindow = Find("TextExtractor", 10000, true);
Assert.IsTrue(true, "Language selection changed successfully through right-click menu");
}
Assert.IsNotNull(textExtractorWindow, "TextExtractor window should be found after hotkey activation");
Console.WriteLine("✓ TextExtractor window detected successfully after hotkey activation");
SendKeys(Key.Esc);
}
// Close the TextExtractor overlay
SendKeys(Key.Esc);
Thread.Sleep(1000);
}
[TestMethod("PowerOCR.TextSelectionAndClipboardTest")]
[TestCategory("PowerOCR Selection")]
public void TextSelectionAndClipboardTest()
{
// Clear clipboard first using STA thread
ClearClipboardSafely();
Thread.Sleep(500);
// Activate Text Extractor overlay
SendKeys(Key.Win, Key.Shift, Key.T);
Thread.Sleep(3000);
var textExtractorWindow = Find<Element>(By.AccessibilityId("TextExtractorWindow"), 10000, true);
Assert.IsNotNull(textExtractorWindow, "TextExtractor window should be found after hotkey activation");
// Click on the TextExtractor window to position cursor
textExtractorWindow.Click();
Thread.Sleep(500);
// Get screen dimensions for full screen selection
var primaryScreen = System.Windows.Forms.Screen.PrimaryScreen;
Assert.IsNotNull(primaryScreen, "Primary screen should be available");
var screenWidth = primaryScreen.Bounds.Width;
var screenHeight = primaryScreen.Bounds.Height;
// Define full screen selection area
var startX = 0;
var startY = 0;
var endX = screenWidth;
var endY = screenHeight;
// Perform continuous mouse drag to select entire screen
PerformSeleniumDrag(startX, startY, endX, endY);
Thread.Sleep(3000); // Wait longer for full screen OCR processing
// Verify text was copied to clipboard using STA thread
var clipboardText = GetClipboardTextSafely();
Assert.IsFalse(string.IsNullOrWhiteSpace(clipboardText), "Clipboard should contain extracted text after selection");
// Close the TextExtractor overlay
SendKeys(Key.Esc);
Thread.Sleep(1000);
}
private static void ClearClipboardSafely()
{
var thread = new System.Threading.Thread(() =>
catch (Exception ex)
{
System.Windows.Forms.Clipboard.Clear();
});
thread.SetApartmentState(System.Threading.ApartmentState.STA);
thread.Start();
thread.Join();
}
private static string GetClipboardTextSafely()
{
string result = string.Empty;
var thread = new System.Threading.Thread(() =>
{
try
{
result = System.Windows.Forms.Clipboard.GetText();
}
catch (Exception)
{
result = string.Empty;
}
});
thread.SetApartmentState(System.Threading.ApartmentState.STA);
thread.Start();
thread.Join();
return result;
}
private void PerformSeleniumDrag(int startX, int startY, int endX, int endY)
{
// Use Selenium Actions for proper drag and drop operation
var actions = new Actions(Session.Root);
// Move to start position, click and hold, drag to end position, then release
actions.MoveByOffset(startX, startY)
.ClickAndHold()
.MoveByOffset(endX - startX, endY - startY)
.Release()
.Build()
.Perform();
Console.WriteLine($"Failed to detect TextExtractor window: {ex.Message}");
Assert.Fail("TextExtractor window was not found after hotkey activation");
}
}
}

View File

@@ -1,15 +0,0 @@
## Text Extractor
* Enable Text Extractor. Then:
- [x] Press the activation shortcut and verify the overlay appears.
- [x] Press Escape and verify the overlay disappears.
- [x] Press the activation shortcut and verify the overlay appears.
- [x] Right-click and select Cancel. Verify the overlay disappears.
- [x] Disable Text Extractor and verify that the activation shortcut no longer activates the utility.
* With Text Extractor enabled and activated:
- [x] Try to select text and verify it is copied to the clipboard.
- [x] Try to select a different OCR language by right-clicking and verify the change is applied.
* In a multi-monitor setup with different DPIs on each monitor:
- [ ] Verify text is correctly captured on all monitors.
* Test the different settings and verify they are applied:
- [x] Activation shortcut
- [x] OCR Language

View File

@@ -6,7 +6,6 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:p="clr-namespace:PowerOCR.Properties"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
x:Name="TextExtractorWindow"
Title="TextExtractor"
ui:Design.Background="Transparent"
AllowsTransparency="True"
@@ -88,7 +87,6 @@
<Separator />
<MenuItem
Name="CancelMenuItem"
AutomationProperties.AutomationId="CancelMenuItem"
Click="CancelMenuItem_Click"
Header="{x:Static p:Resources.Cancel}" />
</ContextMenu>
@@ -119,7 +117,6 @@
<ComboBox
x:Name="LanguagesComboBox"
Margin="4,0"
AutomationProperties.AutomationId="OCROverlayLanguagesComboBox"
AutomationProperties.Name="{x:Static p:Resources.SelectedLang}"
SelectionChanged="LanguagesComboBox_SelectionChanged">
<ComboBox.ItemTemplate>

View File

@@ -29,6 +29,7 @@
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
@@ -39,6 +40,7 @@
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Optimization>MaxSpeed</Optimization>
<SDLCheck>false</SDLCheck>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
</ClCompile>

View File

@@ -29,6 +29,7 @@
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
@@ -39,6 +40,7 @@
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Optimization>MaxSpeed</Optimization>
<SDLCheck>false</SDLCheck>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
</ClCompile>

View File

@@ -29,6 +29,7 @@
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
@@ -39,6 +40,7 @@
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Optimization>MaxSpeed</Optimization>
<SDLCheck>false</SDLCheck>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
</ClCompile>

View File

@@ -81,6 +81,7 @@
<Optimization>MaxSpeed</Optimization>
<PreprocessorDefinitions>__ZOOMIT_POWERTOYS__;_UNICODE;UNICODE;WINVER=0x602;NDEBUG;_WIN32_WINNT=0x602;_WIN32_WINDOWS=0x501;WIN32;_WINDOWS;MSVC6=1;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
</ClCompile>
<ResourceCompile>
@@ -102,6 +103,7 @@
<Optimization>MaxSpeed</Optimization>
<PreprocessorDefinitions>__ZOOMIT_POWERTOYS__;_UNICODE;UNICODE;WINVER=0x602;NDEBUG;_WIN32_WINNT=0x602;_WIN32_WINDOWS=0x501;WIN32;_WINDOWS;MSVC6=1;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
</ClCompile>
<ResourceCompile>
@@ -124,6 +126,7 @@
<Optimization>MaxSpeed</Optimization>
<PreprocessorDefinitions>__ZOOMIT_POWERTOYS__;_UNICODE;UNICODE;WINVER=0x602;NDEBUG;_WIN32_WINNT=0x602;_WIN32_WINDOWS=0x501;WIN32;_WINDOWS;MSVC6=1;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
</ClCompile>
<ResourceCompile>
@@ -145,6 +148,7 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>__ZOOMIT_POWERTOYS__;_UNICODE;UNICODE;WINVER=0x0602;_DEBUG;_WIN32_WINNT=0x602.MSVC6;_WIN32_WINDOWS=0x600;WIN32;_WINDOWS;_WIN32_WINNT=0x602;MSVC6=1;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>_DEBUG;_M_IX86;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@@ -165,6 +169,7 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>__ZOOMIT_POWERTOYS__;_UNICODE;UNICODE;WINVER=0x0602;_DEBUG;_WIN32_WINNT=0x602.MSVC6;_WIN32_WINDOWS=0x600;WIN32;_WINDOWS;_WIN32_WINNT=0x602;MSVC6=1;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>_DEBUG;_M_X64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@@ -186,6 +191,7 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>__ZOOMIT_POWERTOYS__;_UNICODE;UNICODE;WINVER=0x0602;_DEBUG;_WIN32_WINNT=0x602.MSVC6;_WIN32_WINDOWS=0x600;WIN32;_WINDOWS;_WIN32_WINNT=0x602;MSVC6=1;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>_DEBUG;_M_ARM64;%(PreprocessorDefinitions)</PreprocessorDefinitions>

View File

@@ -29,6 +29,7 @@
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
@@ -39,6 +40,7 @@
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Optimization>MaxSpeed</Optimization>
<SDLCheck>false</SDLCheck>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
</ClCompile>

View File

@@ -141,4 +141,43 @@
<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>
<!-- BEGIN common.build.post.props -->
<!--
The Hybrid CRT model statically links the runtime and STL and dynamically
links the UCRT instead of the VC++ CRT. The UCRT ships with Windows.
WinAppSDK asserts that this is "supported according to the CRT maintainer."
This must come before Microsoft.Cpp.targets because it manipulates ClCompile.RuntimeLibrary.
-->
<ItemDefinitionGroup Condition="'$(EnableHybridCRT)'=='true' and '$(Configuration)'=='Debug'">
<ClCompile>
<!-- We use MultiThreadedDebug, rather than MultiThreadedDebugDLL, to avoid DLL dependencies on VCRUNTIME140d.dll and MSVCP140d.dll. -->
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<!-- Link statically against the runtime and STL, but link dynamically against the CRT by ignoring the static CRT
lib and instead linking against the Universal CRT DLL import library. This "hybrid" linking mechanism is
supported according to the CRT maintainer. Dynamic linking against the CRT makes the binaries a bit smaller
than they would otherwise be if the CRT, runtime, and STL were all statically linked in. -->
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries);libucrtd.lib</IgnoreSpecificDefaultLibraries>
<AdditionalOptions>%(AdditionalOptions) /defaultlib:ucrtd.lib</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(EnableHybridCRT)'=='true' and ('$(Configuration)'=='Release' or '$(Configuration)'=='AuditMode')">
<ClCompile>
<!-- We use MultiThreaded, rather than MultiThreadedDLL, to avoid DLL dependencies on VCRUNTIME140.dll and MSVCP140.dll. -->
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<!-- Link statically against the runtime and STL, but link dynamically against the CRT by ignoring the static CRT
lib and instead linking against the Universal CRT DLL import library. This "hybrid" linking mechanism is
supported according to the CRT maintainer. Dynamic linking against the CRT makes the binaries a bit smaller
than they would otherwise be if the CRT, runtime, and STL were all statically linked in. -->
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries);libucrt.lib</IgnoreSpecificDefaultLibraries>
<AdditionalOptions>%(AdditionalOptions) /defaultlib:ucrt.lib</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<!-- END common.build.post.props -->
</Project>

View File

@@ -306,7 +306,6 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
Command.PropertyChanged -= Command_PropertyChanged;
Command = new(model.Command, PageContext);
Command.InitializeProperties();
Command.PropertyChanged += Command_PropertyChanged;
// Extensions based on Command Palette SDK < 0.3 CommandItem class won't notify when Title changes because Command
// or Command.Name change. This is a workaround to ensure that the Title is always up-to-date for extensions with old SDK.

View File

@@ -97,17 +97,6 @@ public partial class ListViewModel : PageViewModel, IDisposable
EmptyContent = new(new(null), PageContext);
}
private void FiltersPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(FiltersViewModel.Filters))
{
var filtersViewModel = sender as FiltersViewModel;
var hasFilters = filtersViewModel?.Filters.Length > 0;
HasFilters = hasFilters;
UpdateProperty(nameof(HasFilters));
}
}
// TODO: Does this need to hop to a _different_ thread, so that we don't block the extension while we're fetching?
private void Model_ItemsChanged(object sender, IItemsChangedEventArgs args) => FetchItems();
@@ -597,11 +586,8 @@ public partial class ListViewModel : PageViewModel, IDisposable
EmptyContent = new(new(model.EmptyContent), PageContext);
EmptyContent.SlowInitializeProperties();
Filters?.PropertyChanged -= FiltersPropertyChanged;
Filters = new(new(model.Filters), PageContext);
Filters?.PropertyChanged += FiltersPropertyChanged;
Filters?.InitializeProperties();
Filters.InitializeProperties();
UpdateProperty(nameof(Filters));
FetchItems();
@@ -700,10 +686,8 @@ public partial class ListViewModel : PageViewModel, IDisposable
EmptyContent.SlowInitializeProperties();
break;
case nameof(Filters):
Filters?.PropertyChanged -= FiltersPropertyChanged;
Filters = new(new(model.Filters), PageContext);
Filters?.PropertyChanged += FiltersPropertyChanged;
Filters?.InitializeProperties();
Filters.InitializeProperties();
break;
case nameof(IsLoading):
UpdateEmptyContent();
@@ -773,7 +757,6 @@ public partial class ListViewModel : PageViewModel, IDisposable
FilteredItems.Clear();
}
Filters?.PropertyChanged -= FiltersPropertyChanged;
Filters?.SafeCleanup();
var model = _model.Unsafe;

View File

@@ -1,12 +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.
namespace Microsoft.CmdPal.Core.ViewModels.Messages;
/// <summary>
/// Used to navigate down one page in the page when pressing the PageDown key in the SearchBox.
/// </summary>
public record NavigatePageDownCommand
{
}

View File

@@ -1,12 +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.
namespace Microsoft.CmdPal.Core.ViewModels.Messages;
/// <summary>
/// Used to navigate up one page in the page when pressing the PageUp key in the SearchBox.
/// </summary>
public record NavigatePageUpCommand
{
}

View File

@@ -68,9 +68,7 @@ public partial class PageViewModel : ExtensionObjectViewModel, IPageContext
// `IsLoading` property as a combo of this value and `IsInitialized`
public bool ModelIsLoading { get; protected set; } = true;
public bool HasSearchBox { get; protected set; } = true;
public bool HasFilters { get; protected set; }
public bool HasSearchBox { get; protected set; }
public IconInfoViewModel Icon { get; protected set; }

View File

@@ -2,7 +2,6 @@
// 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.ComponentModel;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
@@ -49,9 +48,6 @@ public partial class ShellViewModel : ObservableObject,
var oldValue = _currentPage;
if (SetProperty(ref _currentPage, value))
{
oldValue.PropertyChanged -= CurrentPage_PropertyChanged;
value.PropertyChanged += CurrentPage_PropertyChanged;
if (oldValue is IDisposable disposable)
{
try
@@ -67,14 +63,6 @@ public partial class ShellViewModel : ObservableObject,
}
}
private void CurrentPage_PropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(PageViewModel.HasSearchBox))
{
IsSearchBoxVisible = CurrentPage.HasSearchBox;
}
}
private IPage? _rootPage;
private bool _isNested;

View File

@@ -1,7 +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.
namespace Microsoft.CmdPal.UI.ViewModels.Messages;
public record ReloadFinishedMessage();

View File

@@ -410,23 +410,5 @@ namespace Microsoft.CmdPal.UI.ViewModels.Properties {
return ResourceManager.GetString("builtin_reload_subtitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to {0} extensions found.
/// </summary>
public static string builtin_settings_extension_n_extensions_found {
get {
return ResourceManager.GetString("builtin_settings_extension_n_extensions_found", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to {0} extensions installed.
/// </summary>
public static string builtin_settings_extension_n_extensions_installed {
get {
return ResourceManager.GetString("builtin_settings_extension_n_extensions_installed", resourceCulture);
}
}
}
}

View File

@@ -236,10 +236,4 @@
<data name="builtin_home_name" xml:space="preserve">
<value>Home</value>
</data>
<data name="builtin_settings_extension_n_extensions_found" xml:space="preserve">
<value>{0} extensions found</value>
</data>
<data name="builtin_settings_extension_n_extensions_installed" xml:space="preserve">
<value>{0} extensions installed</value>
</data>
</root>

View File

@@ -1,144 +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.ObjectModel;
using System.Collections.Specialized;
using System.Globalization;
using System.Text;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.CmdPal.Core.ViewModels.Messages;
using Microsoft.CmdPal.UI.ViewModels.Messages;
using Microsoft.CommandPalette.Extensions.Toolkit;
namespace Microsoft.CmdPal.UI.ViewModels;
/// <summary>
/// Provides filtering over the list of provider settings view models.
/// Intended to be used by the UI to bind a TextBox (SearchText) and an ItemsRepeater (FilteredProviders).
/// </summary>
public partial class SettingsExtensionsViewModel : ObservableObject
{
private static readonly CompositeFormat LabelNumberExtensionFound
= CompositeFormat.Parse(Properties.Resources.builtin_settings_extension_n_extensions_found!);
private static readonly CompositeFormat LabelNumberExtensionInstalled
= CompositeFormat.Parse(Properties.Resources.builtin_settings_extension_n_extensions_installed!);
private readonly ObservableCollection<ProviderSettingsViewModel> _source;
private readonly TaskScheduler _uiScheduler;
public ObservableCollection<ProviderSettingsViewModel> FilteredProviders { get; } = [];
private string _searchText = string.Empty;
public string SearchText
{
get => _searchText;
set
{
if (_searchText != value)
{
_searchText = value;
OnPropertyChanged();
ApplyFilter();
}
}
}
public string ItemCounterText
{
get
{
var hasQuery = !string.IsNullOrWhiteSpace(_searchText);
var count = hasQuery ? FilteredProviders.Count : _source.Count;
var format = hasQuery ? LabelNumberExtensionFound : LabelNumberExtensionInstalled;
return string.Format(CultureInfo.CurrentCulture, format, count);
}
}
public bool ShowManualReloadOverlay
{
get;
private set
{
if (field != value)
{
field = value;
OnPropertyChanged();
}
}
}
public bool ShowNoResultsPanel => !string.IsNullOrWhiteSpace(_searchText) && FilteredProviders.Count == 0;
public bool HasResults => !ShowNoResultsPanel;
public IRelayCommand ReloadExtensionsCommand { get; }
public SettingsExtensionsViewModel(ObservableCollection<ProviderSettingsViewModel> source, TaskScheduler uiScheduler)
{
_source = source;
_uiScheduler = uiScheduler;
_source.CollectionChanged += Source_CollectionChanged;
ApplyFilter();
ReloadExtensionsCommand = new RelayCommand(ReloadExtensions);
WeakReferenceMessenger.Default.Register<ReloadFinishedMessage>(this, (_, _) =>
{
Task.Factory.StartNew(() => ShowManualReloadOverlay = false, CancellationToken.None, TaskCreationOptions.None, _uiScheduler);
});
}
private void Source_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
ApplyFilter();
}
private void ApplyFilter()
{
var query = _searchText;
var filtered = ListHelpers.FilterList(_source, query, Matches);
ListHelpers.InPlaceUpdateList(FilteredProviders, filtered);
OnPropertyChanged(nameof(ItemCounterText));
OnPropertyChanged(nameof(HasResults));
OnPropertyChanged(nameof(ShowNoResultsPanel));
}
private static int Matches(string query, ProviderSettingsViewModel item)
{
if (string.IsNullOrWhiteSpace(query))
{
return 100;
}
return Contains(item.DisplayName, query)
|| Contains(item.ExtensionName, query)
|| Contains(item.ExtensionSubtext, query)
? 100
: 0;
}
private static bool Contains(string? haystack, string needle)
{
return !string.IsNullOrEmpty(haystack) && haystack.Contains(needle, StringComparison.OrdinalIgnoreCase);
}
[RelayCommand]
private void OpenStoreWithExtension(string? query)
{
const string extensionsAssocUri = "ms-windows-store://assoc/?Tags=AppExtension-com.microsoft.commandpalette";
ShellHelpers.OpenInShell(extensionsAssocUri);
}
private void ReloadExtensions()
{
ShowManualReloadOverlay = true;
WeakReferenceMessenger.Default.Send<ClearSearchMessage>();
WeakReferenceMessenger.Default.Send<ReloadCommandsMessage>();
}
}

View File

@@ -54,8 +54,6 @@ public partial class SettingsModel : ObservableObject
public bool DisableAnimations { get; set; } = true;
public WindowPosition? LastWindowPosition { get; set; }
// END SETTINGS
///////////////////////////////////////////////////////////////////////////
@@ -193,7 +191,6 @@ public partial class SettingsModel : ObservableObject
[JsonSerializable(typeof(bool))]
[JsonSerializable(typeof(HistoryItem))]
[JsonSerializable(typeof(SettingsModel))]
[JsonSerializable(typeof(WindowPosition))]
[JsonSerializable(typeof(AppStateModel))]
[JsonSerializable(typeof(RecentCommandsManager))]
[JsonSerializable(typeof(List<string>), TypeInfoPropertyName = "StringList")]
@@ -211,5 +208,4 @@ public enum MonitorBehavior
ToPrimary = 1,
ToFocusedWindow = 2,
InPlace = 3,
ToLast = 4,
}

View File

@@ -140,8 +140,6 @@ public partial class SettingsViewModel : INotifyPropertyChanged
public ObservableCollection<ProviderSettingsViewModel> CommandProviders { get; } = [];
public SettingsExtensionsViewModel Extensions { get; }
public SettingsViewModel(SettingsModel settings, IServiceProvider serviceProvider, TaskScheduler scheduler)
{
_settings = settings;
@@ -157,8 +155,6 @@ public partial class SettingsViewModel : INotifyPropertyChanged
var settingsModel = new ProviderSettingsViewModel(item, providerSettings, _serviceProvider);
CommandProviders.Add(settingsModel);
}
Extensions = new SettingsExtensionsViewModel(CommandProviders, scheduler);
}
private IEnumerable<CommandProviderWrapper> GetCommandProviders()

View File

@@ -259,9 +259,6 @@ public partial class TopLevelCommandManager : ObservableObject,
IsLoading = false;
// Send on the current thread; receivers should marshal to UI if needed
WeakReferenceMessenger.Default.Send<ReloadFinishedMessage>();
return true;
}

View File

@@ -219,7 +219,7 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem
{
PropChanged?.Invoke(this, new PropChangedEventArgs(e.PropertyName));
if (e.PropertyName is "IsInitialized" or nameof(CommandItemViewModel.Command))
if (e.PropertyName == "IsInitialized")
{
GenerateId();

View File

@@ -1,22 +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.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.CmdPal.UI.ViewModels;
public sealed class WindowPosition
{
public int X { get; set; }
public int Y { get; set; }
public int Width { get; set; }
public int Height { get; set; }
}

View File

@@ -40,8 +40,6 @@ namespace Microsoft.CmdPal.UI;
/// </summary>
public partial class App : Application
{
private readonly GlobalErrorHandler _globalErrorHandler = new();
/// <summary>
/// Gets the current <see cref="App"/> instance in use.
/// </summary>
@@ -63,10 +61,6 @@ public partial class App : Application
/// </summary>
public App()
{
#if !CMDPAL_DISABLE_GLOBAL_ERROR_HANDLER
_globalErrorHandler.Register(this);
#endif
Services = ConfigureServices();
this.InitializeComponent();

View File

@@ -1,100 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_125_2801)">
<g clip-path="url(#clip1_125_2801)">
<path d="M0 5C0 4.44771 0.447715 4 1 4H22C22.5523 4 23 4.44772 23 5V18.5C23 20.9853 20.9853 23 18.5 23H4.5C2.01472 23 0 20.9853 0 18.5V5Z" fill="url(#paint0_linear_125_2801)"/>
<mask id="mask0_125_2801" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="4" width="23" height="19">
<path d="M0 5C0 4.44771 0.447715 4 1 4H22C22.5523 4 23 4.44772 23 5V18.5C23 20.9853 20.9853 23 18.5 23H4.5C2.01472 23 0 20.9853 0 18.5V5Z" fill="url(#paint1_linear_125_2801)"/>
</mask>
<g mask="url(#mask0_125_2801)">
<g filter="url(#filter0_dd_125_2801)">
<path d="M6 5V1H17V5" stroke="url(#paint2_linear_125_2801)" stroke-width="2" stroke-linecap="round"/>
</g>
</g>
<rect x="6" y="8" width="11" height="11" rx="1" fill="white" fill-opacity="0.5"/>
<g filter="url(#filter1_d_125_2801)">
<path d="M11 9H7V13H11V9Z" fill="#F25022"/>
<path d="M16 9H12V13H16V9Z" fill="#7FBA00"/>
<path d="M16 14H12V18H16V14Z" fill="#FFB900"/>
<path d="M11 14H7V18H11V14Z" fill="#00A4EF"/>
</g>
<path d="M6 5V2C6 1.44772 6.44772 1 7 1H16C16.5523 1 17 1.44772 17 2V5" stroke="url(#paint3_linear_125_2801)" stroke-width="2" stroke-linecap="round"/>
<rect x="7" width="9" height="2" fill="url(#paint4_linear_125_2801)"/>
<mask id="mask1_125_2801" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="7" y="0" width="9" height="2">
<rect x="7" width="9" height="2" fill="url(#paint5_linear_125_2801)"/>
</mask>
<g mask="url(#mask1_125_2801)">
<g filter="url(#filter2_dd_125_2801)">
</g>
</g>
</g>
</g>
<defs>
<filter id="filter0_dd_125_2801" x="3.5" y="-1" width="16" height="9" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.25"/>
<feGaussianBlur stdDeviation="0.25"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_125_2801"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.5"/>
<feGaussianBlur stdDeviation="0.75"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2 0"/>
<feBlend mode="normal" in2="effect1_dropShadow_125_2801" result="effect2_dropShadow_125_2801"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_125_2801" result="shape"/>
</filter>
<filter id="filter1_d_125_2801" x="6" y="8.25" width="11" height="11" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.25"/>
<feGaussianBlur stdDeviation="0.5"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_125_2801"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_125_2801" result="shape"/>
</filter>
<filter id="filter2_dd_125_2801" x="3.5" y="-2.5" width="16" height="8" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="-1"/>
<feGaussianBlur stdDeviation="0.25"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_125_2801"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="-1"/>
<feGaussianBlur stdDeviation="0.75"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2 0"/>
<feBlend mode="normal" in2="effect1_dropShadow_125_2801" result="effect2_dropShadow_125_2801"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_125_2801" result="shape"/>
</filter>
<linearGradient id="paint0_linear_125_2801" x1="5.22727" y1="1.19318" x2="9.5894" y2="22.315" gradientUnits="userSpaceOnUse">
<stop offset="0.270833" stop-color="white"/>
<stop offset="1" stop-color="#DFDFDF"/>
</linearGradient>
<linearGradient id="paint1_linear_125_2801" x1="11.5" y1="4" x2="15.4941" y2="23.743" gradientUnits="userSpaceOnUse">
<stop stop-color="#0078D4"/>
<stop offset="1" stop-color="#114A8B"/>
</linearGradient>
<linearGradient id="paint2_linear_125_2801" x1="11.5" y1="1" x2="11.8823" y2="5.29249" gradientUnits="userSpaceOnUse">
<stop stop-color="#28AFEA"/>
<stop offset="1" stop-color="#0078D4"/>
</linearGradient>
<linearGradient id="paint3_linear_125_2801" x1="11.5" y1="1" x2="11.7158" y2="4.23049" gradientUnits="userSpaceOnUse">
<stop stop-color="#30DAFF"/>
<stop offset="1" stop-color="#0094D4"/>
</linearGradient>
<linearGradient id="paint4_linear_125_2801" x1="11" y1="5.23303e-08" x2="11" y2="2" gradientUnits="userSpaceOnUse">
<stop stop-color="#22BCFF"/>
<stop offset="1" stop-color="#0088F0"/>
</linearGradient>
<linearGradient id="paint5_linear_125_2801" x1="11" y1="5.23303e-08" x2="11" y2="2" gradientUnits="userSpaceOnUse">
<stop stop-color="#28AFEA"/>
<stop offset="1" stop-color="#3CCBF4"/>
</linearGradient>
<clipPath id="clip0_125_2801">
<rect width="24" height="24" fill="white"/>
</clipPath>
<clipPath id="clip1_125_2801">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 5.6 KiB

View File

@@ -1,100 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_125_2875)">
<g clip-path="url(#clip1_125_2875)">
<path d="M0 5C0 4.44771 0.447715 4 1 4H22C22.5523 4 23 4.44772 23 5V18.5C23 20.9853 20.9853 23 18.5 23H4.5C2.01472 23 0 20.9853 0 18.5V5Z" fill="url(#paint0_linear_125_2875)"/>
<mask id="mask0_125_2875" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="4" width="23" height="19">
<path d="M0 5C0 4.44771 0.447715 4 1 4H22C22.5523 4 23 4.44772 23 5V18.5C23 20.9853 20.9853 23 18.5 23H4.5C2.01472 23 0 20.9853 0 18.5V5Z" fill="url(#paint1_linear_125_2875)"/>
</mask>
<g mask="url(#mask0_125_2875)">
<g filter="url(#filter0_dd_125_2875)">
<path d="M6 5V1H17V5" stroke="url(#paint2_linear_125_2875)" stroke-width="2" stroke-linecap="round"/>
</g>
</g>
<rect x="6" y="8" width="11" height="11" rx="1" fill="#243A5F" fill-opacity="0.5"/>
<g filter="url(#filter1_d_125_2875)">
<path d="M11 9H7V13H11V9Z" fill="#F25022"/>
<path d="M16 9H12V13H16V9Z" fill="#7FBA00"/>
<path d="M16 14H12V18H16V14Z" fill="#FFB900"/>
<path d="M11 14H7V18H11V14Z" fill="#00A4EF"/>
</g>
<path d="M6 5V2C6 1.44772 6.44772 1 7 1H16C16.5523 1 17 1.44772 17 2V5" stroke="url(#paint3_linear_125_2875)" stroke-width="2" stroke-linecap="round"/>
<rect x="7" width="9" height="2" fill="url(#paint4_linear_125_2875)"/>
<mask id="mask1_125_2875" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="7" y="0" width="9" height="2">
<rect x="7" width="9" height="2" fill="url(#paint5_linear_125_2875)"/>
</mask>
<g mask="url(#mask1_125_2875)">
<g filter="url(#filter2_dd_125_2875)">
</g>
</g>
</g>
</g>
<defs>
<filter id="filter0_dd_125_2875" x="3.5" y="-1" width="16" height="9" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.25"/>
<feGaussianBlur stdDeviation="0.25"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_125_2875"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.5"/>
<feGaussianBlur stdDeviation="0.75"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2 0"/>
<feBlend mode="normal" in2="effect1_dropShadow_125_2875" result="effect2_dropShadow_125_2875"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_125_2875" result="shape"/>
</filter>
<filter id="filter1_d_125_2875" x="6" y="8.25" width="11" height="11" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.25"/>
<feGaussianBlur stdDeviation="0.5"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_125_2875"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_125_2875" result="shape"/>
</filter>
<filter id="filter2_dd_125_2875" x="3.5" y="-2.5" width="16" height="8" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="-1"/>
<feGaussianBlur stdDeviation="0.25"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_125_2875"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="-1"/>
<feGaussianBlur stdDeviation="0.75"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2 0"/>
<feBlend mode="normal" in2="effect1_dropShadow_125_2875" result="effect2_dropShadow_125_2875"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_125_2875" result="shape"/>
</filter>
<linearGradient id="paint0_linear_125_2875" x1="11.5" y1="4" x2="15.4941" y2="23.743" gradientUnits="userSpaceOnUse">
<stop stop-color="#0669BC"/>
<stop offset="1" stop-color="#243A5F"/>
</linearGradient>
<linearGradient id="paint1_linear_125_2875" x1="11.5" y1="4" x2="15.4941" y2="23.743" gradientUnits="userSpaceOnUse">
<stop stop-color="#0078D4"/>
<stop offset="1" stop-color="#114A8B"/>
</linearGradient>
<linearGradient id="paint2_linear_125_2875" x1="11.5" y1="1" x2="11.8823" y2="5.29249" gradientUnits="userSpaceOnUse">
<stop stop-color="#28AFEA"/>
<stop offset="1" stop-color="#0078D4"/>
</linearGradient>
<linearGradient id="paint3_linear_125_2875" x1="11.5" y1="1" x2="11.7158" y2="4.23049" gradientUnits="userSpaceOnUse">
<stop stop-color="#30DAFF"/>
<stop offset="1" stop-color="#0094D4"/>
</linearGradient>
<linearGradient id="paint4_linear_125_2875" x1="11" y1="5.23303e-08" x2="11" y2="2" gradientUnits="userSpaceOnUse">
<stop stop-color="#22BCFF"/>
<stop offset="1" stop-color="#0088F0"/>
</linearGradient>
<linearGradient id="paint5_linear_125_2875" x1="11" y1="5.23303e-08" x2="11" y2="2" gradientUnits="userSpaceOnUse">
<stop stop-color="#28AFEA"/>
<stop offset="1" stop-color="#3CCBF4"/>
</linearGradient>
<clipPath id="clip0_125_2875">
<rect width="24" height="24" fill="white"/>
</clipPath>
<clipPath id="clip1_125_2875">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 5.6 KiB

View File

@@ -1,97 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_125_2968)">
<path d="M3 1.5C3 0.671573 3.67157 0 4.5 0H19.5C20.3284 0 21 0.671573 21 1.5V16.5C21 17.3284 20.3284 18 19.5 18H4.5C3.67157 18 3 17.3284 3 16.5V1.5Z" fill="#9C640A"/>
<mask id="mask0_125_2968" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="3" y="0" width="18" height="18">
<path d="M3 1.5C3 0.671573 3.67157 0 4.5 0H19.5C20.3284 0 21 0.671573 21 1.5V16.5C21 17.3284 20.3284 18 19.5 18H4.5C3.67157 18 3 17.3284 3 16.5V1.5Z" fill="#9C640A"/>
</mask>
<g mask="url(#mask0_125_2968)">
<g filter="url(#filter0_dd_125_2968)">
<path d="M1.5 4.5C1.5 3.67157 2.17157 3 3 3H21C21.8284 3 22.5 3.67157 22.5 4.5V19.5C22.5 20.3284 21.8284 21 21 21H3C2.17157 21 1.5 20.3284 1.5 19.5V4.5Z" fill="#BC822A"/>
</g>
</g>
<path d="M1.5 4.5C1.5 3.67157 2.17157 3 3 3H21C21.8284 3 22.5 3.67157 22.5 4.5V19.5C22.5 20.3284 21.8284 21 21 21H3C2.17157 21 1.5 20.3284 1.5 19.5V4.5Z" fill="#BC822A"/>
<mask id="mask1_125_2968" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="1" y="3" width="22" height="18">
<path d="M1.5 4.5C1.5 3.67157 2.17157 3 3 3H21C21.8284 3 22.5 3.67157 22.5 4.5V19.5C22.5 20.3284 21.8284 21 21 21H3C2.17157 21 1.5 20.3284 1.5 19.5V4.5Z" fill="#BC822A"/>
</mask>
<g mask="url(#mask1_125_2968)">
<g filter="url(#filter1_dd_125_2968)">
<path d="M0 7.5C0 6.67157 0.671573 6 1.5 6H22.5C23.3284 6 24 6.67157 24 7.5V22.5C24 23.3284 23.3284 24 22.5 24H1.5C0.671573 24 0 23.3284 0 22.5V7.5Z" fill="#D9D9D9"/>
<path d="M0 7.5C0 6.67157 0.671573 6 1.5 6H22.5C23.3284 6 24 6.67157 24 7.5V22.5C24 23.3284 23.3284 24 22.5 24H1.5C0.671573 24 0 23.3284 0 22.5V7.5Z" fill="url(#paint0_linear_125_2968)"/>
<path d="M0 7.5C0 6.67157 0.671573 6 1.5 6H22.5C23.3284 6 24 6.67157 24 7.5V22.5C24 23.3284 23.3284 24 22.5 24H1.5C0.671573 24 0 23.3284 0 22.5V7.5Z" fill="url(#paint1_linear_125_2968)"/>
</g>
</g>
<path d="M0 7.5C0 6.67157 0.671573 6 1.5 6H22.5C23.3284 6 24 6.67157 24 7.5V22.5C24 23.3284 23.3284 24 22.5 24H1.5C0.671573 24 0 23.3284 0 22.5V7.5Z" fill="#D9D9D9"/>
<path d="M0 7.5C0 6.67157 0.671573 6 1.5 6H22.5C23.3284 6 24 6.67157 24 7.5V22.5C24 23.3284 23.3284 24 22.5 24H1.5C0.671573 24 0 23.3284 0 22.5V7.5Z" fill="url(#paint2_linear_125_2968)"/>
<path d="M0 7.5C0 6.67157 0.671573 6 1.5 6H22.5C23.3284 6 24 6.67157 24 7.5V22.5C24 23.3284 23.3284 24 22.5 24H1.5C0.671573 24 0 23.3284 0 22.5V7.5Z" fill="url(#paint3_linear_125_2968)"/>
<g filter="url(#filter2_dd_125_2968)">
<path d="M12 9C12.8284 9 13.5 9.67157 13.5 10.5V15.8789L14.6895 14.6895C15.2752 14.1037 16.2248 14.1037 16.8105 14.6895C17.3963 15.2752 17.3963 16.2248 16.8105 16.8105L13.0605 20.5605C12.4748 21.1463 11.5252 21.1463 10.9395 20.5605L7.18945 16.8105C6.60367 16.2248 6.60367 15.2752 7.18945 14.6895C7.77524 14.1037 8.72476 14.1037 9.31055 14.6895L10.5 15.8789V10.5C10.5 9.67157 11.1716 9 12 9Z" fill="url(#paint4_linear_125_2968)"/>
</g>
</g>
<defs>
<filter id="filter0_dd_125_2968" x="0.5" y="1.505" width="23" height="20" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="-0.495"/>
<feGaussianBlur stdDeviation="0.5"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.32 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_125_2968"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="-0.105"/>
<feGaussianBlur stdDeviation="0.05"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0"/>
<feBlend mode="normal" in2="effect1_dropShadow_125_2968" result="effect2_dropShadow_125_2968"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_125_2968" result="shape"/>
</filter>
<filter id="filter1_dd_125_2968" x="-1" y="4.505" width="26" height="20" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="-0.495"/>
<feGaussianBlur stdDeviation="0.5"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.32 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_125_2968"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="-0.105"/>
<feGaussianBlur stdDeviation="0.05"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0"/>
<feBlend mode="normal" in2="effect1_dropShadow_125_2968" result="effect2_dropShadow_125_2968"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_125_2968" result="shape"/>
</filter>
<filter id="filter2_dd_125_2968" x="5.75" y="8.5" width="12.5" height="14" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.5"/>
<feGaussianBlur stdDeviation="0.5"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.32 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_125_2968"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.1"/>
<feGaussianBlur stdDeviation="0.05"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.24 0"/>
<feBlend mode="normal" in2="effect1_dropShadow_125_2968" result="effect2_dropShadow_125_2968"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_125_2968" result="shape"/>
</filter>
<linearGradient id="paint0_linear_125_2968" x1="0" y1="6" x2="17.28" y2="29.04" gradientUnits="userSpaceOnUse">
<stop stop-color="#DEB678"/>
<stop offset="1" stop-color="#C59141"/>
</linearGradient>
<linearGradient id="paint1_linear_125_2968" x1="6.75" y1="3.75" x2="14.25" y2="26.25" gradientUnits="userSpaceOnUse">
<stop stop-color="#DEB678"/>
<stop offset="1" stop-color="#B57F2D"/>
</linearGradient>
<linearGradient id="paint2_linear_125_2968" x1="0" y1="6" x2="17.28" y2="29.04" gradientUnits="userSpaceOnUse">
<stop stop-color="#DEB678"/>
<stop offset="1" stop-color="#C59141"/>
</linearGradient>
<linearGradient id="paint3_linear_125_2968" x1="6.75" y1="3.75" x2="14.25" y2="26.25" gradientUnits="userSpaceOnUse">
<stop stop-color="#DEB678"/>
<stop offset="1" stop-color="#B57F2D"/>
</linearGradient>
<linearGradient id="paint4_linear_125_2968" x1="9.13631" y1="7.22729" x2="12.8104" y2="20.0865" gradientUnits="userSpaceOnUse">
<stop offset="0.270833" stop-color="white"/>
<stop offset="1" stop-color="#F0F0F0"/>
</linearGradient>
<clipPath id="clip0_125_2968">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 6.9 KiB

View File

@@ -77,7 +77,7 @@
SelectedValue="{x:Bind ViewModel.CurrentFilter, Mode=OneWay}"
SelectionChanged="FiltersComboBox_SelectionChanged"
Style="{StaticResource ComboBoxStyle}"
Visibility="{x:Bind ViewModel.ShouldShowFilters, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}, FallbackValue=Collapsed}">
Visibility="{x:Bind ViewModel.ShouldShowFilters, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
<ComboBox.ItemContainerStyle>
<Style BasedOn="{StaticResource DefaultComboBoxItemStyle}" TargetType="ComboBoxItem">
<Setter Property="MinHeight" Value="0" />

View File

@@ -216,16 +216,6 @@ public sealed partial class SearchBar : UserControl,
e.Handled = true;
}
else if (e.Key == VirtualKey.PageDown)
{
WeakReferenceMessenger.Default.Send<NavigatePageDownCommand>();
e.Handled = true;
}
else if (e.Key == VirtualKey.PageUp)
{
WeakReferenceMessenger.Default.Send<NavigatePageUpCommand>();
e.Handled = true;
}
if (InSuggestion)
{

View File

@@ -18,7 +18,6 @@ using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using Windows.Foundation;
using Windows.System;
namespace Microsoft.CmdPal.UI;
@@ -26,8 +25,6 @@ namespace Microsoft.CmdPal.UI;
public sealed partial class ListPage : Page,
IRecipient<NavigateNextCommand>,
IRecipient<NavigatePreviousCommand>,
IRecipient<NavigatePageDownCommand>,
IRecipient<NavigatePageUpCommand>,
IRecipient<ActivateSelectedListItemMessage>,
IRecipient<ActivateSecondaryCommandMessage>
{
@@ -85,8 +82,6 @@ public sealed partial class ListPage : Page,
// RegisterAll isn't AOT compatible
WeakReferenceMessenger.Default.Register<NavigateNextCommand>(this);
WeakReferenceMessenger.Default.Register<NavigatePreviousCommand>(this);
WeakReferenceMessenger.Default.Register<NavigatePageDownCommand>(this);
WeakReferenceMessenger.Default.Register<NavigatePageUpCommand>(this);
WeakReferenceMessenger.Default.Register<ActivateSelectedListItemMessage>(this);
WeakReferenceMessenger.Default.Register<ActivateSecondaryCommandMessage>(this);
@@ -99,8 +94,6 @@ public sealed partial class ListPage : Page,
WeakReferenceMessenger.Default.Unregister<NavigateNextCommand>(this);
WeakReferenceMessenger.Default.Unregister<NavigatePreviousCommand>(this);
WeakReferenceMessenger.Default.Unregister<NavigatePageDownCommand>(this);
WeakReferenceMessenger.Default.Unregister<NavigatePageUpCommand>(this);
WeakReferenceMessenger.Default.Unregister<ActivateSelectedListItemMessage>(this);
WeakReferenceMessenger.Default.Unregister<ActivateSecondaryCommandMessage>(this);
@@ -188,9 +181,9 @@ public sealed partial class ListPage : Page,
var notificationText = li.Title;
UIHelper.AnnounceActionForAccessibility(
ItemsList,
notificationText,
"CommandPaletteSelectedItemChanged");
ItemsList,
notificationText,
"CommandPaletteSelectedItemChanged");
}
}
}
@@ -303,142 +296,6 @@ public sealed partial class ListPage : Page,
}
}
public void Receive(NavigatePageDownCommand message)
{
var indexes = CalculateTargetIndexPageUpDownScrollTo(true);
if (indexes is null)
{
return;
}
if (indexes.Value.CurrentIndex != indexes.Value.TargetIndex)
{
ItemView.SelectedIndex = indexes.Value.TargetIndex;
ItemView.ScrollIntoView(ItemView.SelectedItem);
}
}
public void Receive(NavigatePageUpCommand message)
{
var indexes = CalculateTargetIndexPageUpDownScrollTo(false);
if (indexes is null)
{
return;
}
if (indexes.Value.CurrentIndex != indexes.Value.TargetIndex)
{
ItemView.SelectedIndex = indexes.Value.TargetIndex;
ItemView.ScrollIntoView(ItemView.SelectedItem);
}
}
/// <summary>
/// Calculates the item index to target when performing a page up or page down
/// navigation. The calculation attempts to estimate how many items fit into
/// the visible viewport by measuring actual container heights currently visible
/// within the internal ScrollViewer. If measurements are not available a
/// fallback estimate is used.
/// </summary>
/// <param name="isPageDown">True to calculate a page-down target, false for page-up.</param>
/// <returns>
/// A tuple containing the current index and the calculated target index, or null
/// if a valid calculation could not be performed (for example, missing ScrollViewer).
/// </returns>
private (int CurrentIndex, int TargetIndex)? CalculateTargetIndexPageUpDownScrollTo(bool isPageDown)
{
var scroll = FindScrollViewer(ItemView);
if (scroll is null)
{
return null;
}
var viewportHeight = scroll.ViewportHeight;
if (viewportHeight <= 0)
{
return null;
}
var currentIndex = ItemView.SelectedIndex < 0 ? 0 : ItemView.SelectedIndex;
var itemCount = ItemView.Items.Count;
// Compute visible item heights within the ScrollViewer viewport
const int firstVisibleIndexNotFound = -1;
var firstVisibleIndex = firstVisibleIndexNotFound;
var visibleHeights = new List<double>(itemCount);
for (var i = 0; i < itemCount; i++)
{
if (ItemView.ContainerFromIndex(i) is FrameworkElement container)
{
try
{
var transform = container.TransformToVisual(scroll);
var topLeft = transform.TransformPoint(new Point(0, 0));
var bottom = topLeft.Y + container.ActualHeight;
// If any part of the container is inside the viewport, consider it visible
if (topLeft.Y >= 0 && bottom <= viewportHeight)
{
if (firstVisibleIndex == firstVisibleIndexNotFound)
{
firstVisibleIndex = i;
}
visibleHeights.Add(container.ActualHeight > 0 ? container.ActualHeight : 0);
}
}
catch
{
// ignore transform errors and continue
}
}
}
var itemsPerPage = 0;
// Calculate how many items fit in the viewport based on their actual heights
if (visibleHeights.Count > 0)
{
double accumulated = 0;
for (var i = 0; i < visibleHeights.Count; i++)
{
accumulated += visibleHeights[i] <= 0 ? 1 : visibleHeights[i];
itemsPerPage++;
if (accumulated >= viewportHeight)
{
break;
}
}
}
else
{
// fallback: estimate using first measured container height
double itemHeight = 0;
for (var i = currentIndex; i < itemCount; i++)
{
if (ItemView.ContainerFromIndex(i) is FrameworkElement { ActualHeight: > 0 } c)
{
itemHeight = c.ActualHeight;
break;
}
}
if (itemHeight <= 0)
{
itemHeight = 1;
}
itemsPerPage = Math.Max(1, (int)Math.Floor(viewportHeight / itemHeight));
}
var targetIndex = isPageDown
? Math.Min(itemCount - 1, currentIndex + Math.Max(1, itemsPerPage))
: Math.Max(0, currentIndex - Math.Max(1, itemsPerPage));
return (currentIndex, targetIndex);
}
private static void OnViewModelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is ListPage @this)
@@ -494,11 +351,11 @@ public sealed partial class ListPage : Page,
}
}
private static ScrollViewer? FindScrollViewer(DependencyObject parent)
private ScrollViewer? FindScrollViewer(DependencyObject parent)
{
if (parent is ScrollViewer viewer)
if (parent is ScrollViewer)
{
return viewer;
return (ScrollViewer)parent;
}
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)

View File

@@ -1,134 +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 ManagedCommon;
using Microsoft.CmdPal.Core.Common.Helpers;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.UI.WindowsAndMessaging;
using SystemUnhandledExceptionEventArgs = System.UnhandledExceptionEventArgs;
using XamlUnhandledExceptionEventArgs = Microsoft.UI.Xaml.UnhandledExceptionEventArgs;
namespace Microsoft.CmdPal.UI.Helpers;
/// <summary>
/// Global error handler for Command Palette.
/// </summary>
internal sealed partial class GlobalErrorHandler
{
// GlobalErrorHandler is designed to be self-contained; it can be registered and invoked before a service provider is available.
internal void Register(App app)
{
ArgumentNullException.ThrowIfNull(app);
app.UnhandledException += App_UnhandledException;
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
}
private void App_UnhandledException(object sender, XamlUnhandledExceptionEventArgs e)
{
// Exceptions thrown on the main UI thread are handled here.
if (e.Exception != null)
{
HandleException(e.Exception, Context.MainThreadException);
}
}
private void CurrentDomain_UnhandledException(object sender, SystemUnhandledExceptionEventArgs e)
{
// Exceptions thrown on background threads are handled here.
if (e.ExceptionObject is Exception ex)
{
HandleException(ex, Context.AppDomainUnhandledException);
}
}
private void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e)
{
// This event is raised only when a faulted Task is garbage-collected
// without its exception being observed. It is NOT raised immediately
// when the Task faults; timing depends on GC finalization.
e.SetObserved();
HandleException(e.Exception, Context.UnobservedTaskException, isRecoverable: true);
}
private void HandleException(Exception ex, Context context, bool isRecoverable = false)
{
Logger.LogError($"Unhandled exception detected ({context})", ex);
if (context == Context.MainThreadException)
{
var error = DiagnosticsHelper.BuildExceptionMessage(ex, null);
var report = $"""
This is an error report generated by Windows Command Palette.
If you are seeing this message, it means the application has encountered an unexpected issue.
You can help us fix it by filing a report at https://aka.ms/powerToysReportBug.
{error}
""";
StoreReport(report, storeOnDesktop: false);
PInvoke.MessageBox(
HWND.Null,
"Command Palette has encountered a fatal error and must close.\n\nAn error report has been saved to your desktop.",
"Unhandled Error",
MESSAGEBOX_STYLE.MB_ICONERROR);
}
}
private static string? StoreReport(string report, bool storeOnDesktop)
{
// Generate a unique name for the report file; include timestamp and a random zero-padded number to avoid collisions
// in case of crash storm.
var name = FormattableString.Invariant($"CmdPal_ErrorReport_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{Random.Shared.Next(100000):D5}.log");
// Always store a copy in log directory, this way it is available for Bug Report Tool
string? reportPath = null;
if (Logger.CurrentVersionLogDirectoryPath != null)
{
reportPath = Save(report, name, static () => Logger.CurrentVersionLogDirectoryPath);
}
// Optionally store a copy on the desktop for user (in)convenience
if (storeOnDesktop)
{
var path = Save(report, name, static () => Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory));
// show the desktop copy if both succeeded
if (path != null)
{
reportPath = path;
}
}
return reportPath;
static string? Save(string reportContent, string reportFileName, Func<string> directory)
{
try
{
var logDirectory = directory();
Directory.CreateDirectory(logDirectory);
var reportFilePath = Path.Combine(logDirectory, reportFileName);
File.WriteAllText(reportFilePath, reportContent);
return reportFilePath;
}
catch (Exception ex)
{
Logger.LogError("Failed to store exception report", ex);
return null;
}
}
}
private enum Context
{
Unknown = 0,
MainThreadException,
BackgroundThreadException,
UnobservedTaskException,
AppDomainUnhandledException,
}
}

View File

@@ -22,7 +22,7 @@ internal static class WindowExtensions
appWindow.SetIcon(@"Assets\icon.ico");
}
public static HWND GetWindowHwnd(this Window window)
private static HWND GetWindowHwnd(this Window window)
{
return window is null
? throw new ArgumentNullException(nameof(window))

View File

@@ -1,89 +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 Microsoft.CmdPal.UI.Helpers;
using Microsoft.UI.Xaml;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Dwm;
using Windows.Win32.UI.WindowsAndMessaging;
namespace Microsoft.CmdPal.UI;
/// <summary>
/// Provides behavior to control taskbar and Alt+Tab presence by assigning a hidden owner
/// and toggling extended window styles for a target window.
/// </summary>
internal sealed class HiddenOwnerWindowBehavior
{
private HWND _hiddenOwnerHwnd;
private Window? _hiddenWindow;
/// <summary>
/// Shows or hides a window in the taskbar (and Alt+Tab) by updating ownership and extended window styles.
/// </summary>
/// <param name="target">The <see cref="Microsoft.UI.Xaml.Window"/> to update.</param>
/// <param name="isVisibleInTaskbar"> True to show the window in the taskbar (and Alt+Tab); false to hide it from both. </param>
/// <remarks>
/// When hiding the window, a hidden owner is assigned and <see cref="WINDOW_EX_STYLE.WS_EX_TOOLWINDOW"/>
/// is enabled to keep it out of the taskbar and Alt+Tab. When showing, the owner is cleared and
/// <see cref="WINDOW_EX_STYLE.WS_EX_APPWINDOW"/> is enabled to ensure taskbar presence. Since tool
/// windows use smaller corner radii, the normal rounded corners are enforced via
/// <see cref="DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUND"/>.
/// </remarks>
/// <seealso href="https://learn.microsoft.com/en-us/windows/win32/shell/taskbar#managing-taskbar-buttons" />
public void ShowInTaskbar(Window target, bool isVisibleInTaskbar)
{
/*
* There are the three main ways to control whether a window appears on the taskbar:
* https://learn.microsoft.com/en-us/windows/win32/shell/taskbar#managing-taskbar-buttons
*
* 1. Set the window's owner. Owned windows do not appear on the taskbar:
* Turns out this is the most reliable way to hide a window from the taskbar and ALT+TAB. WinForms and WPF uses this method
* to back their ShowInTaskbar property as well.
*
* 2. Use the WS_EX_TOOLWINDOW extended window style:
* This mostly works, with some reports that it silently fails in some cases. The biggest issue
* is that for certain Windows settings (like Multitasking -> Show taskbar buttons on all displays = On all desktops),
* the taskbar button is always shown even for tool windows.
*
* 3. Using ITaskbarList:
* This is what AppWindow.IsShownInSwitchers uses, but it's COM-based and more complex, and can
* fail if Explorer isn't running or responding. It could be a good backup, if needed.
*/
var visibleHwnd = target.GetWindowHwnd();
if (isVisibleInTaskbar)
{
// remove any owner window
PInvoke.SetWindowLongPtr(visibleHwnd, WINDOW_LONG_PTR_INDEX.GWLP_HWNDPARENT, HWND.Null);
}
else
{
// Set the hidden window as the owner of the target window
var hiddenHwnd = EnsureHiddenOwner();
PInvoke.SetWindowLongPtr(visibleHwnd, WINDOW_LONG_PTR_INDEX.GWLP_HWNDPARENT, hiddenHwnd);
}
// Tool windows don't show up in ALT+TAB, and don't show up in the taskbar
// Tool window and app window styles are mutually exclusive, change both just to be safe
target.ToggleExtendedWindowStyle(WINDOW_EX_STYLE.WS_EX_TOOLWINDOW, !isVisibleInTaskbar);
target.ToggleExtendedWindowStyle(WINDOW_EX_STYLE.WS_EX_APPWINDOW, isVisibleInTaskbar);
// Since tool windows have smaller corner radii, we need to force the normal ones
target.SetCornerPreference(DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUND);
}
private HWND EnsureHiddenOwner()
{
if (_hiddenOwnerHwnd.IsNull)
{
_hiddenWindow = new Window();
_hiddenOwnerHwnd = _hiddenWindow.GetWindowHwnd();
}
return _hiddenOwnerHwnd;
}
}

View File

@@ -57,18 +57,14 @@ public sealed partial class MainWindow : WindowEx,
private readonly List<TopLevelHotkey> _hotkeys = [];
private readonly KeyboardListener _keyboardListener;
private readonly LocalKeyboardListener _localKeyboardListener;
private readonly HiddenOwnerWindowBehavior _hiddenOwnerBehavior = new();
private bool _ignoreHotKeyWhenFullScreen = true;
private DesktopAcrylicController? _acrylicController;
private SystemBackdropConfiguration? _configurationSource;
private WindowPosition _currentWindowPosition = new();
public MainWindow()
{
InitializeComponent();
HideWindow();
_hwnd = new HWND(WinRT.Interop.WindowNative.GetWindowHandle(this).ToInt32());
@@ -77,8 +73,6 @@ public sealed partial class MainWindow : WindowEx,
CommandPaletteHost.SetHostHwnd((ulong)_hwnd.Value);
}
_hiddenOwnerBehavior.ShowInTaskbar(this, Debugger.IsAttached);
_keyboardListener = new KeyboardListener();
_keyboardListener.Start();
@@ -86,9 +80,7 @@ public sealed partial class MainWindow : WindowEx,
this.SetIcon();
AppWindow.Title = RS_.GetString("AppName");
RestoreWindowPosition();
UpdateWindowPositionInMemory();
PositionCentered();
SetAcrylic();
WeakReferenceMessenger.Default.Register<DismissMessage>(this);
@@ -134,6 +126,16 @@ public sealed partial class MainWindow : WindowEx,
// Force window to be created, and then cloaked. This will offset initial animation when the window is shown.
HideWindow();
ApplyWindowStyle();
}
private void ApplyWindowStyle()
{
// Tool windows don't show up in ALT+TAB, and don't show up in the taskbar
// Since tool windows have smaller corner radii, we need to force the normal ones
this.ToggleExtendedWindowStyle(WINDOW_EX_STYLE.WS_EX_TOOLWINDOW, !Debugger.IsAttached);
this.SetCornerPreference(DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUND);
}
private static void LocalKeyboardListener_OnKeyPressed(object? sender, LocalKeyboardListenerKeyPressedEventArgs e)
@@ -159,39 +161,6 @@ public sealed partial class MainWindow : WindowEx,
PositionCentered(displayArea);
}
private void RestoreWindowPosition()
{
var settings = App.Current.Services.GetService<SettingsModel>();
if (settings?.LastWindowPosition is not WindowPosition savedPosition)
{
PositionCentered();
return;
}
if (savedPosition.Width <= 0 || savedPosition.Height <= 0)
{
PositionCentered();
return;
}
AppWindow.Resize(new SizeInt32 { Width = savedPosition.Width, Height = savedPosition.Height });
var savedRect = new RectInt32(savedPosition.X, savedPosition.Y, savedPosition.Width, savedPosition.Height);
var displayArea = DisplayArea.GetFromRect(savedRect, DisplayAreaFallback.Nearest);
var workArea = displayArea.WorkArea;
var maxX = workArea.X + Math.Max(0, workArea.Width - savedPosition.Width);
var maxY = workArea.Y + Math.Max(0, workArea.Height - savedPosition.Height);
var targetPoint = new PointInt32
{
X = Math.Clamp(savedPosition.X, workArea.X, maxX),
Y = Math.Clamp(savedPosition.Y, workArea.Y, maxY),
};
AppWindow.Move(targetPoint);
}
private void PositionCentered(DisplayArea displayArea)
{
if (displayArea is not null)
@@ -206,17 +175,6 @@ public sealed partial class MainWindow : WindowEx,
}
}
private void UpdateWindowPositionInMemory()
{
_currentWindowPosition = new WindowPosition
{
X = AppWindow.Position.X,
Y = AppWindow.Position.Y,
Width = AppWindow.Size.Width,
Height = AppWindow.Size.Height,
};
}
private void HotReloadSettings()
{
var settings = App.Current.Services.GetService<SettingsModel>()!;
@@ -299,22 +257,14 @@ public sealed partial class MainWindow : WindowEx,
PInvoke.ShowWindow(hwnd, SHOW_WINDOW_CMD.SW_RESTORE);
}
if (target == MonitorBehavior.ToLast)
{
AppWindow.Resize(new SizeInt32 { Width = _currentWindowPosition.Width, Height = _currentWindowPosition.Height });
AppWindow.Move(new PointInt32 { X = _currentWindowPosition.X, Y = _currentWindowPosition.Y });
}
else
{
var display = GetScreen(hwnd, target);
PositionCentered(display);
}
var display = GetScreen(hwnd, target);
PositionCentered(display);
// Check if the debugger is attached. If it is, we don't want to apply the tool window style,
// because that would make it hard to debug the app
if (Debugger.IsAttached)
{
_hiddenOwnerBehavior.ShowInTaskbar(this, true);
ApplyWindowStyle();
}
// Just to be sure, SHOW our hwnd.
@@ -469,22 +419,6 @@ public sealed partial class MainWindow : WindowEx,
internal void MainWindow_Closed(object sender, WindowEventArgs args)
{
var serviceProvider = App.Current.Services;
UpdateWindowPositionInMemory();
var settings = serviceProvider.GetService<SettingsModel>();
if (settings is not null)
{
settings.LastWindowPosition = new WindowPosition
{
X = _currentWindowPosition.X,
Y = _currentWindowPosition.Y,
Width = _currentWindowPosition.Width,
Height = _currentWindowPosition.Height,
};
SettingsModel.SaveSettings(settings);
}
var extensionService = serviceProvider.GetService<IExtensionService>()!;
extensionService.SignalStopExtensionsAsync();
@@ -556,9 +490,6 @@ public sealed partial class MainWindow : WindowEx,
{
if (args.WindowActivationState == WindowActivationState.Deactivated)
{
// Save the current window position before hiding the window
UpdateWindowPositionInMemory();
// If there's a debugger attached...
if (System.Diagnostics.Debugger.IsAttached)
{

View File

@@ -111,10 +111,6 @@
<ProjectCapability Include="Msix" />
</ItemGroup>
<ItemGroup>
<SDKReference Include="Microsoft.VCLibs.Desktop, Version=14.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" />
<ProjectReference Include="..\ext\Microsoft.CmdPal.Ext.ClipboardHistory\Microsoft.CmdPal.Ext.ClipboardHistory.csproj" />
@@ -185,15 +181,6 @@
<Content Update="..\Microsoft.CmdPal.UI.ViewModels\Assets\template.zip">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Update="Assets\StoreLogo.dark.svg">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
<Content Update="Assets\StoreLogo.light.svg">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
<Content Update="Assets\WinGetLogo.svg">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>

View File

@@ -58,9 +58,4 @@ GetModuleHandle
GetWindowLong
SetWindowLong
WINDOW_EX_STYLE
CreateWindowEx
WNDCLASSEXW
RegisterClassEx
GetStockObject
GetModuleHandle
WINDOW_EX_STYLE

View File

@@ -48,8 +48,7 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
IRecipient<ShowConfirmationMessage>,
IRecipient<ShowToastMessage>,
IRecipient<NavigateToPageMessage>,
INotifyPropertyChanged,
IDisposable
INotifyPropertyChanged
{
private readonly DispatcherQueue _queue = DispatcherQueue.GetForCurrentThread();
@@ -66,9 +65,6 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
private SettingsWindow? _settingsWindow;
private CancellationTokenSource? _focusAfterLoadedCts;
private WeakReference<Page>? _lastNavigatedPageRef;
public ShellViewModel ViewModel { get; private set; } = App.Current.Services.GetService<ShellViewModel>()!;
public event PropertyChangedEventHandler? PropertyChanged;
@@ -451,15 +447,7 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
{
while (RootFrame.CanGoBack)
{
// don't focus on each step, just at the end
GoBack(withAnimation, focusSearch: false);
}
// focus search box, even if we were already home
if (focusSearch)
{
SearchBox.Focus(Microsoft.UI.Xaml.FocusState.Programmatic);
SearchBox.SelectSearch();
GoBack(withAnimation, focusSearch);
}
}
@@ -500,7 +488,6 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
if (e.Content is Page element)
{
_lastNavigatedPageRef = new WeakReference<Page>(element);
element.Loaded += FocusAfterLoaded;
}
}
@@ -510,18 +497,6 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
var page = (Page)sender;
page.Loaded -= FocusAfterLoaded;
// Only handle focus for the latest navigated page
if (_lastNavigatedPageRef is null || !_lastNavigatedPageRef.TryGetTarget(out var last) || !ReferenceEquals(page, last))
{
return;
}
// Cancel any previous pending focus work
_focusAfterLoadedCts?.Cancel();
_focusAfterLoadedCts?.Dispose();
_focusAfterLoadedCts = new CancellationTokenSource();
var token = _focusAfterLoadedCts.Token;
AnnounceNavigationToPage(page);
var shouldSearchBoxBeVisible = ViewModel.CurrentPage?.HasSearchBox ?? false;
@@ -534,57 +509,34 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
}
else
{
_ = Task.Run(
async () =>
_ = Task.Run(async () =>
{
await page.DispatcherQueue.EnqueueAsync(async () =>
{
if (token.IsCancellationRequested)
// I hate this so much, but it can take a while for the page to be ready to accept focus;
// focusing page with MarkdownTextBlock takes up to 5 attempts (* 100ms delay between attempts)
for (var i = 0; i < 10; i++)
{
return;
}
try
{
await page.DispatcherQueue.EnqueueAsync(
async () =>
if (FocusManager.FindFirstFocusableElement(page) is FrameworkElement frameworkElement)
{
var set = frameworkElement.Focus(FocusState.Programmatic);
if (set)
{
// I hate this so much, but it can take a while for the page to be ready to accept focus;
// focusing page with MarkdownTextBlock takes up to 5 attempts (* 100ms delay between attempts)
for (var i = 0; i < 10; i++)
{
token.ThrowIfCancellationRequested();
break;
}
}
if (FocusManager.FindFirstFocusableElement(page) is FrameworkElement frameworkElement)
{
var set = frameworkElement.Focus(FocusState.Programmatic);
if (set)
{
break;
}
}
await Task.Delay(100, token);
}
token.ThrowIfCancellationRequested();
// Update the search box visibility based on the current page:
// - We do this here after navigation so the focus is not jumping around too much,
// it messes with screen readers if we do it too early
// - Since this should hide the search box on content pages, it's not a problem if we
// wait for the code above to finish trying to focus the content
ViewModel.IsSearchBoxVisible = ViewModel.CurrentPage?.HasSearchBox ?? false;
});
await Task.Delay(100);
}
catch (OperationCanceledException)
{
// Swallow cancellation - another FocusAfterLoaded invocation superseded this one
}
catch (Exception ex)
{
Logger.LogError("Error during FocusAfterLoaded async focus work", ex);
}
},
token);
// Update the search box visibility based on the current page:
// - We do this here after navigation so the focus is not jumping around too much,
// it messes with screen readers if we do it too early
// - Since this should hide the search box on content pages, it's not a problem if we
// wait for the code above to finish trying to focus the content
ViewModel.IsSearchBoxVisible = ViewModel.CurrentPage?.HasSearchBox ?? false;
});
});
}
}
@@ -639,31 +591,24 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.RightWindows).HasFlag(CoreVirtualKeyStates.Down);
var onlyAlt = altPressed && !ctrlPressed && !shiftPressed && !winPressed;
var onlyCtrl = !altPressed && ctrlPressed && !shiftPressed && !winPressed;
switch (e.Key)
if (e.Key == VirtualKey.Left && onlyAlt)
{
case VirtualKey.Left when onlyAlt: // Alt+Left arrow
WeakReferenceMessenger.Default.Send<NavigateBackMessage>(new());
e.Handled = true;
break;
case VirtualKey.Home when onlyAlt: // Alt+Home
WeakReferenceMessenger.Default.Send<GoHomeMessage>(new(WithAnimation: false));
e.Handled = true;
break;
case (VirtualKey)188 when onlyCtrl: // Ctrl+,
WeakReferenceMessenger.Default.Send<OpenSettingsMessage>(new());
e.Handled = true;
break;
default:
{
// The CommandBar is responsible for handling all the item keybindings,
// since the bound context item may need to then show another
// context menu
TryCommandKeybindingMessage msg = new(ctrlPressed, altPressed, shiftPressed, winPressed, e.Key);
WeakReferenceMessenger.Default.Send(msg);
e.Handled = msg.Handled;
break;
}
WeakReferenceMessenger.Default.Send<NavigateBackMessage>(new());
e.Handled = true;
}
else if (e.Key == VirtualKey.Home && onlyAlt)
{
WeakReferenceMessenger.Default.Send<GoHomeMessage>(new(WithAnimation: false));
e.Handled = true;
}
else
{
// The CommandBar is responsible for handling all the item keybindings,
// since the bound context item may need to then show another
// context menu
TryCommandKeybindingMessage msg = new(ctrlPressed, altPressed, shiftPressed, winPressed, e.Key);
WeakReferenceMessenger.Default.Send(msg);
e.Handled = msg.Handled;
}
}
@@ -713,11 +658,4 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
Logger.LogError("Error handling mouse button press event", ex);
}
}
public void Dispose()
{
_focusAfterLoadedCts?.Cancel();
_focusAfterLoadedCts?.Dispose();
_focusAfterLoadedCts = null;
}
}

View File

@@ -14,204 +14,18 @@
xmlns:viewModels="using:Microsoft.CmdPal.UI.ViewModels"
mc:Ignorable="d">
<Page.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<!-- ControlFillColorQuarternaryBrush does not exist (yet) in WinUI, only in Windows Visual Library (Figma) -->
<ResourceDictionary x:Key="Default">
<LinearGradientBrush x:Key="CardGradient2Brush" StartPoint="0,0" EndPoint="0.5, 1">
<GradientStop Offset="0" Color="#38C8AEC4" />
<GradientStop Offset="1" Color="#383286EE" />
</LinearGradientBrush>
<ImageSource x:Key="StoreLogo">ms-appx:///Assets/StoreLogo.dark.svg</ImageSource>
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<LinearGradientBrush x:Key="CardGradient2Brush" StartPoint="0,0" EndPoint="1, 1">
<!--<GradientStop Offset="0" Color="#E6F0FC" />
<GradientStop Offset="0.4" Color="#E6F0FC" />
<GradientStop Offset="1" Color="#F0F0F7" />-->
<GradientStop Offset="0.0" Color="#FFF6F9FF" />
<!-- Light cool white -->
<GradientStop Offset="0.4" Color="#FFEFF5FF" />
<!-- Hinted lavender/blue -->
<GradientStop Offset="0.7" Color="#FFF7FAFD" />
<!-- Very soft neutral white -->
<GradientStop Offset="1.0" Color="#FFF5F8FA" />
<!-- Slight bluish-gray tint -->
<!-- Faint peach glow -->
<!-- Soft peach -->
</LinearGradientBrush>
<ImageSource x:Key="StoreLogo">ms-appx:///Assets/StoreLogo.light.svg</ImageSource>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<SolidColorBrush x:Key="CardGradient2Brush" Color="Transparent" />
<SolidColorBrush x:Key="CardGradient1Brush" Color="Transparent" />
<ImageSource x:Key="StoreLogo">ms-appx:///Assets/StoreLogo.dark.svg</ImageSource>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<converters:BoolNegationConverter x:Key="InvertedBoolConverter" />
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
</ResourceDictionary>
</Page.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ScrollViewer x:Name="RootScrollViewer" Grid.Row="1">
<ScrollViewer Grid.Row="1">
<Grid Padding="16">
<StackPanel
MaxWidth="1000"
HorizontalAlignment="Stretch"
Spacing="{StaticResource SettingsCardSpacing}">
<!-- Banner -->
<Grid
Padding="16"
Background="{ThemeResource CardGradient2Brush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="8">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel x:Name="BannerDescriptionPanel" Orientation="Vertical">
<TextBlock x:Uid="Settings_ExtensionsPage_Banner_Header" FontWeight="SemiBold" />
<TextBlock
x:Uid="Settings_ExtensionsPage_Banner_Description"
Grid.Row="1"
MaxWidth="520"
HorizontalAlignment="Left"
TextWrapping="Wrap" />
<HyperlinkButton
x:Uid="Settings_ExtensionsPage_Banner_Hyperlink"
Padding="0"
Content="Learn how to create your own extensions"
NavigateUri="https://learn.microsoft.com/windows/powertoys/command-palette/overview" />
</StackPanel>
<StackPanel
x:Name="ButtonPanel"
Grid.Column="1"
Orientation="Horizontal"
Spacing="8">
<Button x:Uid="Settings_ExtensionsPage_FindExtensions_MicrosoftStore" Command="{x:Bind viewModel.Extensions.OpenStoreWithExtensionCommand}">
<StackPanel Orientation="Horizontal" Spacing="8">
<Viewbox Width="16">
<Image AutomationProperties.AccessibilityView="Raw" Source="{ThemeResource StoreLogo}" />
</Viewbox>
<TextBlock Text="Microsoft Store" />
</StackPanel>
</Button>
<!--<Button x:Uid="Settings_ExtensionsPage_FindExtensions_WinGet" Command="{x:Bind viewModel.Extensions.OpenStoreWithExtensionCommand}">
<StackPanel Orientation="Horizontal" Spacing="8">
<Viewbox Width="16">
<Image AutomationProperties.AccessibilityView="Raw" Source="ms-appx:///Assets/WinGet.svg" />
</Viewbox>
<TextBlock Text="WinGet" />
</StackPanel>
</Button>-->
</StackPanel>
</Grid>
<!-- Search -->
<Grid Padding="0,24,0,8" ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" MaxWidth="320" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<AutoSuggestBox
x:Name="SearchBox"
x:Uid="Settings_ExtensionsPage_SearchBox_Placeholder"
HorizontalAlignment="Stretch"
Text="{x:Bind viewModel.Extensions.SearchText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<AutoSuggestBox.QueryIcon>
<SymbolIcon Symbol="Find" />
</AutoSuggestBox.QueryIcon>
<AutoSuggestBox.KeyboardAccelerators>
<KeyboardAccelerator
Key="F"
Invoked="OnFindInvoked"
Modifiers="Control" />
</AutoSuggestBox.KeyboardAccelerators>
</AutoSuggestBox>
<StackPanel
Grid.Column="1"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Orientation="Horizontal"
Spacing="8">
<TextBlock
x:Name="ItemCounterTextBlock"
VerticalAlignment="Center"
Style="{StaticResource BodyTextBlockStyle}"
Text="{x:Bind viewModel.Extensions.ItemCounterText, Mode=OneWay}" />
<StackPanel
VerticalAlignment="Center"
Orientation="Horizontal"
Spacing="8"
Visibility="{x:Bind viewModel.Extensions.ShowManualReloadOverlay, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
<ProgressRing
Width="16"
Height="16"
IsActive="True" />
<TextBlock
x:Uid="Settings_ExtensionsPage_Reloading_Text"
VerticalAlignment="Center"
Style="{StaticResource BodyTextBlockStyle}" />
</StackPanel>
<Button
x:Name="MoreButton"
x:Uid="Settings_ExtensionsPage_More_Button"
Style="{StaticResource SubtleButtonStyle}">
<Button.Flyout>
<MenuFlyout Placement="BottomEdgeAlignedRight">
<MenuFlyoutItem x:Uid="Settings_ExtensionsPage_More_Reload_MenuFlyoutItem" Command="{x:Bind viewModel.Extensions.ReloadExtensionsCommand}">
<MenuFlyoutItem.Icon>
<FontIcon Glyph="&#xE777;" />
</MenuFlyoutItem.Icon>
</MenuFlyoutItem>
</MenuFlyout>
</Button.Flyout>
<FontIcon
AutomationProperties.AccessibilityView="Raw"
FontSize="16"
Glyph="&#xE712;" />
</Button>
</StackPanel>
</Grid>
<!-- Empty state when no results match the current search -->
<Grid
x:Name="NoResultsPanel"
Padding="48"
x:Load="{x:Bind viewModel.Extensions.ShowNoResultsPanel, Mode=OneWay}"
CornerRadius="4">
<StackPanel
HorizontalAlignment="Center"
VerticalAlignment="Center"
Spacing="8">
<TextBlock
x:Uid="Settings_ExtensionsPage_NoResults_Primary"
Style="{StaticResource BodyStrongTextBlockStyle}"
TextAlignment="Center" />
<TextBlock
x:Uid="Settings_ExtensionsPage_NoResults_Secondary"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
TextAlignment="Center" />
</StackPanel>
</Grid>
<ItemsRepeater
x:Name="ProvidersRepeater"
x:Load="{x:Bind viewModel.Extensions.HasResults, Mode=OneWay}"
ItemsSource="{x:Bind viewModel.Extensions.FilteredProviders, Mode=OneWay}"
Layout="{StaticResource VerticalStackLayout}">
<ItemsRepeater ItemsSource="{x:Bind viewModel.CommandProviders, Mode=OneWay}" Layout="{StaticResource VerticalStackLayout}">
<ItemsRepeater.ItemTemplate>
<DataTemplate x:DataType="viewModels:ProviderSettingsViewModel">
<controls:SettingsCard
@@ -240,25 +54,5 @@
</StackPanel>
</Grid>
</ScrollViewer>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="LayoutVisualStates">
<VisualState x:Name="WideLayout">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="720" />
</VisualState.StateTriggers>
</VisualState>
<VisualState x:Name="NarrowLayout">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="ButtonPanel.(Grid.Row)" Value="1" />
<Setter Target="BannerDescriptionPanel.(Grid.ColumnSpan)" Value="2" />
<Setter Target="ButtonPanel.(Grid.Column)" Value="0" />
<Setter Target="ButtonPanel.Margin" Value="0,12,0,0" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</Page>

View File

@@ -8,7 +8,6 @@ using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
namespace Microsoft.CmdPal.UI.Settings;
@@ -36,10 +35,4 @@ public sealed partial class ExtensionsPage : Page
}
}
}
private void OnFindInvoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)
{
SearchBox?.Focus(FocusState.Keyboard);
args.Handled = true;
}
}

View File

@@ -63,7 +63,6 @@
<ComboBoxItem x:Uid="Run_Radio_Position_Primary_Monitor" />
<ComboBoxItem x:Uid="Run_Radio_Position_Focus" />
<ComboBoxItem x:Uid="Run_Radio_Position_In_Place" />
<ComboBoxItem x:Uid="Run_Radio_Position_LastPosition" />
</ComboBox>
</controls:SettingsCard>

View File

@@ -25,10 +25,7 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TitleBar
x:Name="AppTitleBar"
IsTabStop="False"
PaneToggleRequested="AppTitleBar_PaneToggleRequested">
<TitleBar x:Name="AppTitleBar" PaneToggleRequested="AppTitleBar_PaneToggleRequested">
<!-- This is a workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/10374, once fixed we should just be using IconSource -->
<TitleBar.LeftHeader>
<ImageIcon

View File

@@ -428,11 +428,7 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
<data name="MoreCommandsButton.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>More</value>
</data>
<data name="Run_Radio_Position_LastPosition.Content" xml:space="preserve">
<value>Last Position</value>
<comment>Reopen the window where it was last closed</comment>
</data>
<data name="TrayMenu_Settings" xml:space="preserve">
<data name="TrayMenu_Settings" xml:space="preserve">
<value>Settings</value>
</data>
<data name="TrayMenu_Close" xml:space="preserve">
@@ -475,46 +471,4 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
<data name="ScreenReader_Announcement_NavigatedToPage0" xml:space="preserve">
<value>Navigated to {0} page</value>
</data>
<data name="SettingsButton.[using:Microsoft.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
<value>Settings (Ctrl+,)</value>
</data>
<data name="Settings_ExtensionsPage_NoResults_Primary.Text" xml:space="preserve">
<value>No extensions found</value>
</data>
<data name="Settings_ExtensionsPage_NoResults_Secondary.Text" xml:space="preserve">
<value>Try a different search term</value>
</data>
<data name="Settings_ExtensionsPage_More_Button.[using:Microsoft.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
<value>More options</value>
</data>
<data name="Settings_ExtensionsPage_More_Reload_MenuFlyoutItem.Text" xml:space="preserve">
<value>Reload extensions</value>
</data>
<data name="Settings_ExtensionsPage_Reloading_Text.Text" xml:space="preserve">
<value>Reloading extensions..</value>
</data>
<data name="Settings_ExtensionsPage_Banner_Header.Text" xml:space="preserve">
<value>Discover more extensions</value>
</data>
<data name="Settings_ExtensionsPage_Banner_Description.Text" xml:space="preserve">
<value>Find more extensions on the Microsoft Store or WinGet.</value>
</data>
<data name="Settings_ExtensionsPage_Banner_Hyperlink.Content" xml:space="preserve">
<value>Learn how to create your own extensions</value>
</data>
<data name="Settings_ExtensionsPage_FindExtensions_MicrosoftStore.[using:Microsoft.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
<value>Find extensions on the Microsoft Store</value>
</data>
<data name="Settings_ExtensionsPage_FindExtensions_MicrosoftStore.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Microsoft Store</value>
</data>
<data name="Settings_ExtensionsPage_FindExtensions_WinGet.[using:Microsoft.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
<value>Find extensions on WinGet</value>
</data>
<data name="Settings_ExtensionsPage_FindExtensions_WinGet.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Microsoft Store</value>
</data>
<data name="Settings_ExtensionsPage_SearchBox_Placeholder.PlaceholderText" xml:space="preserve">
<value>Search extensions</value>
</data>
</root>

View File

@@ -25,11 +25,11 @@ public sealed partial class ToastWindow : WindowEx,
IRecipient<QuitMessage>
{
private readonly HWND _hwnd;
private readonly DispatcherQueueTimer _debounceTimer = DispatcherQueue.GetForCurrentThread().CreateTimer();
private readonly HiddenOwnerWindowBehavior _hiddenOwnerWindowBehavior = new();
public ToastViewModel ViewModel { get; } = new();
private readonly DispatcherQueueTimer _debounceTimer = DispatcherQueue.GetForCurrentThread().CreateTimer();
public ToastWindow()
{
this.InitializeComponent();
@@ -39,7 +39,12 @@ public sealed partial class ToastWindow : WindowEx,
this.SetIcon();
AppWindow.Title = RS_.GetString("ToastWindowTitle");
AppWindow.TitleBar.PreferredHeightOption = TitleBarHeightOption.Collapsed;
_hiddenOwnerWindowBehavior.ShowInTaskbar(this, false);
// Tool windows don't show up in ALT+TAB, and don't show up in the taskbar
// Since tool windows have smaller corner radii, we need to force the normal ones
// to visually match system toasts.
this.ToggleExtendedWindowStyle(WINDOW_EX_STYLE.WS_EX_TOOLWINDOW, true);
this.SetCornerPreference(DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUND);
_hwnd = new HWND(WinRT.Interop.WindowNative.GetWindowHandle(this).ToInt32());
PInvoke.EnableWindow(_hwnd, false);

View File

@@ -74,11 +74,35 @@
<ClCompile>
<LanguageStandard>stdcpp20</LanguageStandard>
</ClCompile>
<ClCompile>
<!-- We use MultiThreadedDebug, rather than MultiThreadedDebugDLL, to avoid DLL dependencies on VCRUNTIME140d.dll and MSVCP140d.dll. -->
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<!-- Link statically against the runtime and STL, but link dynamically against the CRT by ignoring the static CRT
lib and instead linking against the Universal CRT DLL import library. This "hybrid" linking mechanism is
supported according to the CRT maintainer. Dynamic linking against the CRT makes the binaries a bit smaller
than they would otherwise be if the CRT, runtime, and STL were all statically linked in. -->
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries);libucrtd.lib</IgnoreSpecificDefaultLibraries>
<AdditionalOptions>%(AdditionalOptions) /defaultlib:ucrtd.lib /profile /opt:ref /opt:icf</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<LanguageStandard>stdcpp20</LanguageStandard>
</ClCompile>
<ClCompile>
<!-- We use MultiThreaded, rather than MultiThreadedDLL, to avoid DLL dependencies on VCRUNTIME140.dll and MSVCP140.dll. -->
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<!-- Link statically against the runtime and STL, but link dynamically against the CRT by ignoring the static CRT
lib and instead linking against the Universal CRT DLL import library. This "hybrid" linking mechanism is
supported according to the CRT maintainer. Dynamic linking against the CRT makes the binaries a bit smaller
than they would otherwise be if the CRT, runtime, and STL were all statically linked in. -->
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries);libucrt.lib</IgnoreSpecificDefaultLibraries>
<AdditionalOptions>%(AdditionalOptions) /defaultlib:ucrt.lib /profile /opt:ref /opt:icf</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">

View File

@@ -186,48 +186,4 @@ public class BookmarkManagerTests
Assert.AreEqual("D:\\UpdatedPath", updatedBookmark.Bookmark);
Assert.IsTrue(bookmarkUpdatedEventFired);
}
[TestMethod]
public void BookmarkManager_LegacyData_IdsArePersistedAcrossLoads()
{
// Arrange
const string json = """
{
"Data":
[
{ "Name": "C:\\","Bookmark": "C:\\" },
{ "Name": "Bing.com","Bookmark": "https://bing.com" }
]
}
""";
var dataSource = new MockBookmarkDataSource(json);
// First load: IDs should be generated for legacy entries
var manager1 = new BookmarksManager(dataSource);
var firstLoad = manager1.Bookmarks.ToList();
Assert.AreEqual(2, firstLoad.Count);
Assert.AreNotEqual(Guid.Empty, firstLoad[0].Id);
Assert.AreNotEqual(Guid.Empty, firstLoad[1].Id);
// Keep a name->id map to be insensitive to ordering
var firstIdsByName = firstLoad.ToDictionary(b => b.Name, b => b.Id);
// Wait deterministically for async persistence to complete
var wasSaved = dataSource.WaitForSave(1, 5000);
Assert.IsTrue(wasSaved, "Data was not saved within the expected time.");
// Second load: should read back the same IDs from persisted data
var manager2 = new BookmarksManager(dataSource);
var secondLoad = manager2.Bookmarks.ToList();
Assert.AreEqual(2, secondLoad.Count);
var secondIdsByName = secondLoad.ToDictionary(b => b.Name, b => b.Id);
foreach (var kvp in firstIdsByName)
{
Assert.IsTrue(secondIdsByName.ContainsKey(kvp.Key), $"Missing bookmark '{kvp.Key}' after reload.");
Assert.AreEqual(kvp.Value, secondIdsByName[kvp.Key], $"Bookmark '{kvp.Key}' upgraded ID was not persisted across loads.");
}
}
}

View File

@@ -110,7 +110,7 @@ public partial class BookmarkResolverTests
[
new PlaceholderClassificationCase(
Name: "Drive",
Input: "C:\\.",
Input: "C:",
ExpectSuccess: true,
ExpectedKind: CommandKind.Directory,
ExpectedTarget: "C:\\",

View File

@@ -2,8 +2,6 @@
// 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.Threading;
using Microsoft.CmdPal.Ext.Bookmarks.Persistence;
namespace Microsoft.CmdPal.Ext.Bookmarks.UnitTests;
@@ -11,7 +9,6 @@ namespace Microsoft.CmdPal.Ext.Bookmarks.UnitTests;
internal sealed class MockBookmarkDataSource : IBookmarkDataSource
{
private string _jsonData;
private int _saveCount;
public MockBookmarkDataSource(string initialJsonData = "[]")
{
@@ -26,26 +23,5 @@ internal sealed class MockBookmarkDataSource : IBookmarkDataSource
public void SaveBookmarkData(string jsonData)
{
_jsonData = jsonData;
Interlocked.Increment(ref _saveCount);
}
public int SaveCount => Volatile.Read(ref _saveCount);
// Waits until at least expectedMinSaves have occurred or the timeout elapses.
// Returns true if the condition was met, false on timeout.
public bool WaitForSave(int expectedMinSaves = 1, int timeoutMs = 2000)
{
var start = Environment.TickCount;
while (Volatile.Read(ref _saveCount) < expectedMinSaves)
{
if (Environment.TickCount - start > timeoutMs)
{
return false;
}
Thread.Sleep(50);
}
return true;
}
}

View File

@@ -103,33 +103,7 @@ internal sealed partial class BookmarksManager : IDisposable, IBookmarksManager
try
{
var jsonData = _dataSource.GetBookmarkData();
var bookmarksData = _parser.ParseBookmarks(jsonData);
// Upgrade old bookmarks if necessary
// Pre .95 versions did not assign IDs to bookmarks
var upgraded = false;
for (var index = 0; index < bookmarksData.Data.Count; index++)
{
var bookmark = bookmarksData.Data[index];
if (bookmark.Id == Guid.Empty)
{
bookmarksData.Data[index] = bookmark with { Id = Guid.NewGuid() };
upgraded = true;
}
}
lock (_lock)
{
_bookmarksData = bookmarksData;
}
// LOAD BEARING: Save upgraded data back to file
// This ensures that old bookmarks are not repeatedly upgraded on each load,
// as the hotkeys and aliases are tied to the generated bookmark IDs.
if (upgraded)
{
_ = SaveChangesAsync();
}
_bookmarksData = _parser.ParseBookmarks(jsonData);
}
catch (Exception ex)
{

View File

@@ -30,72 +30,25 @@ internal sealed partial class AddBookmarkForm : FormContent
"type": "Input.Text",
"style": "text",
"id": "bookmark",
"value": {{EncodeString(url)}},
"label": {{EncodeString(Resources.bookmarks_form_bookmark_label)}},
"value": {{JsonSerializer.Serialize(url, BookmarkSerializationContext.Default.String)}},
"label": "{{Resources.bookmarks_form_bookmark_label}}",
"isRequired": true,
"errorMessage": {{EncodeString(Resources.bookmarks_form_bookmark_required)}},
"placeholder": {{EncodeString(Resources.bookmarks_form_bookmark_placeholder)}}
"errorMessage": "{{Resources.bookmarks_form_bookmark_required}}"
},
{
"type": "Input.Text",
"style": "text",
"id": "name",
"label": {{EncodeString(Resources.bookmarks_form_name_label)}},
"value": {{EncodeString(name)}},
"isRequired": false
},
{
"type": "RichTextBlock",
"inlines": [
{
"type": "TextRun",
"text": {{EncodeString(Resources.bookmarks_form_hint_text1)}},
"isSubtle": true,
"size": "Small"
},
{
"type": "TextRun",
"text": " ",
"isSubtle": true,
"size": "Small"
},
{
"type": "TextRun",
"text": {{EncodeString(Resources.bookmarks_form_hint_text2)}},
"fontType": "Monospace",
"size": "Small"
},
{
"type": "TextRun",
"text": " ",
"isSubtle": true,
"size": "Small"
},
{
"type": "TextRun",
"text": {{EncodeString(Resources.bookmarks_form_hint_text3)}},
"isSubtle": true,
"size": "Small"
},
{
"type": "TextRun",
"text": " ",
"isSubtle": true,
"size": "Small"
},
{
"type": "TextRun",
"text": {{EncodeString(Resources.bookmarks_form_hint_text4)}},
"fontType": "Monospace",
"size": "Small"
}
]
"label": "{{Resources.bookmarks_form_name_label}}",
"value": {{JsonSerializer.Serialize(name, BookmarkSerializationContext.Default.String)}},
"isRequired": false,
"errorMessage": "{{Resources.bookmarks_form_name_required}}"
}
],
"actions": [
{
"type": "Action.Submit",
"title": {{EncodeString(Resources.bookmarks_form_save)}},
"title": "{{Resources.bookmarks_form_save}}",
"data": {
"name": "name",
"bookmark": "bookmark"
@@ -106,8 +59,6 @@ internal sealed partial class AddBookmarkForm : FormContent
""";
}
private static string EncodeString(string s) => JsonSerializer.Serialize(s, BookmarkSerializationContext.Default.String);
public override CommandResult SubmitForm(string payload)
{
var formInput = JsonNode.Parse(payload);

View File

@@ -19,7 +19,7 @@ public sealed record BookmarkData
[SetsRequiredMembers]
public BookmarkData(Guid id, string? name, string? bookmark)
{
Id = id;
Id = id == Guid.Empty ? Guid.NewGuid() : id;
Name = name ?? string.Empty;
Bookmark = bookmark ?? string.Empty;
}

View File

@@ -19,7 +19,7 @@ namespace Microsoft.CmdPal.Ext.Bookmarks.Properties {
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class Resources {
@@ -177,15 +177,6 @@ namespace Microsoft.CmdPal.Ext.Bookmarks.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Enter URL or file path, you can use {placeholders}, e.g. https://www.bing.com/search?q={Query}.
/// </summary>
public static string bookmarks_form_bookmark_placeholder {
get {
return ResourceManager.GetString("bookmarks_form_bookmark_placeholder", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to URL or file path is required.
/// </summary>
@@ -196,44 +187,7 @@ namespace Microsoft.CmdPal.Ext.Bookmarks.Properties {
}
/// <summary>
/// Looks up a localized string similar to You can add placeholders to bookmarks, and Command Palette will prompt you to enter their values when you open the bookmark.
///A placeholder looks like this:.
/// </summary>
public static string bookmarks_form_hint_text1 {
get {
return ResourceManager.GetString("bookmarks_form_hint_text1", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to {placeholder}.
/// </summary>
public static string bookmarks_form_hint_text2 {
get {
return ResourceManager.GetString("bookmarks_form_hint_text2", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to — for example:.
/// </summary>
public static string bookmarks_form_hint_text3 {
get {
return ResourceManager.GetString("bookmarks_form_hint_text3", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to https://www.bing.com/search?q={Query}.
/// </summary>
public static string bookmarks_form_hint_text4 {
get {
return ResourceManager.GetString("bookmarks_form_hint_text4", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Name (optional).
/// Looks up a localized string similar to Name.
/// </summary>
public static string bookmarks_form_name_label {
get {

View File

@@ -140,7 +140,7 @@
<comment>"Terminal" should be the localized name of the Windows Terminal</comment>
</data>
<data name="bookmarks_form_name_label" xml:space="preserve">
<value>Name (optional)</value>
<value>Name</value>
</data>
<data name="bookmarks_form_save" xml:space="preserve">
<value>Save</value>
@@ -185,20 +185,4 @@
<data name="bookmark_toast_failed_open_text" xml:space="preserve">
<value>Failed to open {0}</value>
</data>
<data name="bookmarks_form_hint_text1" xml:space="preserve">
<value>You can add placeholders to bookmarks, and Command Palette will prompt you to enter their values when you open the bookmark.
A placeholder looks like this:</value>
</data>
<data name="bookmarks_form_hint_text2" xml:space="preserve">
<value>{placeholder}</value>
</data>
<data name="bookmarks_form_hint_text3" xml:space="preserve">
<value>— for example:</value>
</data>
<data name="bookmarks_form_hint_text4" xml:space="preserve">
<value>https://www.bing.com/search?q={Query}</value>
</data>
<data name="bookmarks_form_bookmark_placeholder" xml:space="preserve">
<value>Enter URL or file path, you can use {placeholders}, e.g. https://www.bing.com/search?q={Query}</value>
</data>
</root>

Some files were not shown because too many files have changed in this diff Show More