Compare commits
30 Commits
dabhatti/e
...
v0.80.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5416a9794 | ||
|
|
a41446d9d0 | ||
|
|
9d97ae8498 | ||
|
|
9ecdbb0281 | ||
|
|
3b82ce969e | ||
|
|
ac1adb7c9a | ||
|
|
c3e9881bd2 | ||
|
|
b0c8e8b90c | ||
|
|
a24ffb3168 | ||
|
|
7b89482b94 | ||
|
|
b31eaf3a0b | ||
|
|
794a5db1b0 | ||
|
|
ffb4ece774 | ||
|
|
f23fa3f592 | ||
|
|
818d3e3035 | ||
|
|
bb5fd4970a | ||
|
|
6333e3157e | ||
|
|
baba63542d | ||
|
|
fc3c421735 | ||
|
|
054c5c833b | ||
|
|
576e2a7d2c | ||
|
|
88c2f3022a | ||
|
|
0a316370d8 | ||
|
|
0110d7d244 | ||
|
|
204427d127 | ||
|
|
6e23c5baa1 | ||
|
|
befcd9ba9e | ||
|
|
86afa4ade8 | ||
|
|
3dc0ed100c | ||
|
|
f6e7635a4e |
2
.github/actions/spell-check/allow/names.txt
vendored
@@ -46,6 +46,7 @@ chrdavis
|
||||
Chrzan
|
||||
clayton
|
||||
Coplen
|
||||
craigloewen
|
||||
crutkas
|
||||
damienleroy
|
||||
davidegiacometti
|
||||
@@ -87,6 +88,7 @@ martinchrzan
|
||||
martinmoene
|
||||
Melman
|
||||
Mikhayelyan
|
||||
msft
|
||||
Myrvold
|
||||
Nemeth
|
||||
nielslaute
|
||||
|
||||
7
.github/actions/spell-check/expect.txt
vendored
@@ -54,7 +54,7 @@ Apm
|
||||
APPBARDATA
|
||||
appdata
|
||||
APPEXECLINK
|
||||
appium
|
||||
Appium
|
||||
Applicationcan
|
||||
appmanifest
|
||||
APPNAME
|
||||
@@ -929,6 +929,7 @@ mscorlib
|
||||
msdata
|
||||
msedge
|
||||
MSGFLT
|
||||
msiexec
|
||||
MSIFASTINSTALL
|
||||
MSIHANDLE
|
||||
msiquery
|
||||
@@ -1028,6 +1029,7 @@ NOSIZE
|
||||
NOTIFICATIONSDLL
|
||||
NOTIFYICONDATAW
|
||||
NOTIMPL
|
||||
notlike
|
||||
NOTOPMOST
|
||||
NOTRACK
|
||||
NOTSRCCOPY
|
||||
@@ -1611,6 +1613,7 @@ TRAYMOUSEMESSAGE
|
||||
triaging
|
||||
TRK
|
||||
trl
|
||||
trx
|
||||
Tsd
|
||||
TServer
|
||||
TStr
|
||||
@@ -1712,10 +1715,12 @@ vscdb
|
||||
vsconfig
|
||||
VSCROLL
|
||||
vsetq
|
||||
VSINSTALLDIR
|
||||
VSM
|
||||
vso
|
||||
vsonline
|
||||
vstemplate
|
||||
vstest
|
||||
VSTHRD
|
||||
VSTT
|
||||
vswhere
|
||||
|
||||
33
.github/workflows/similarIssues.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: GitGudSimilarIssues comments
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
getSimilarIssues:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
message: ${{ steps.getBody.outputs.message }}
|
||||
steps:
|
||||
- id: getBody
|
||||
uses: craigloewen-msft/GitGudSimilarIssues@main
|
||||
with:
|
||||
issueTitle: ${{ github.event.issue.title }}
|
||||
issueBody: ${{ github.event.issue.body }}
|
||||
repo: ${{ github.repository }}
|
||||
similarityTolerance: "0.75"
|
||||
add-comment:
|
||||
needs: getSimilarIssues
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
if: needs.getSimilarIssues.outputs.message != ''
|
||||
steps:
|
||||
- name: Add comment
|
||||
run: gh issue comment "$NUMBER" --repo "$REPO" --body "$BODY"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
NUMBER: ${{ github.event.issue.number }}
|
||||
REPO: ${{ github.repository }}
|
||||
BODY: ${{ needs.getSimilarIssues.outputs.message }}
|
||||
2
.gitignore
vendored
@@ -33,6 +33,8 @@ bld/
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
Generated/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
51
.pipelines/ESRPSigning_DSC.json
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"Version": "1.0.0",
|
||||
"UseMinimatch": false,
|
||||
"SignBatches": [
|
||||
{
|
||||
"MatchedPath": [
|
||||
"Microsoft.PowerToys.Configure.psm1",
|
||||
"Microsoft.PowerToys.Configure.psd1"
|
||||
],
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
"*.resources.dll",
|
||||
|
||||
"WinUI3Apps\\Assets\\Settings\\Scripts\\*.ps1",
|
||||
|
||||
|
||||
"PowerToys.ActionRunner.exe",
|
||||
"PowerToys.Update.exe",
|
||||
"PowerToys.BackgroundActivatorDLL.dll",
|
||||
@@ -54,6 +54,7 @@
|
||||
"fancyzones.dll",
|
||||
"PowerToys.FancyZonesEditor.exe",
|
||||
"PowerToys.FancyZonesEditor.dll",
|
||||
"PowerToys.FancyZonesEditorCommon.dll",
|
||||
"PowerToys.FancyZonesModuleInterface.dll",
|
||||
"PowerToys.FancyZones.exe",
|
||||
|
||||
|
||||
15
.pipelines/InstallWinAppDriver.ps1
Normal file
@@ -0,0 +1,15 @@
|
||||
$ProgressPreference = 'SilentlyContinue'
|
||||
|
||||
$WinAppDriverDownloadUrl = "https://github.com/microsoft/WinAppDriver/releases/download/v1.2.1/WindowsApplicationDriver_1.2.1.msi"
|
||||
|
||||
# Download WinAppDriver and verify their hash sums
|
||||
Invoke-WebRequest -Uri $WinAppDriverDownloadUrl -OutFile "$($ENV:Temp)\WindowsApplicationDriver_1.2.1.msi"
|
||||
$Hash = (Get-FileHash -Algorithm SHA256 "$($ENV:Temp)\WindowsApplicationDriver_1.2.1.msi").Hash
|
||||
if ($Hash -ne 'a76a8f4e44b29bad331acf6b6c248fcc65324f502f28826ad2acd5f3c80857fe')
|
||||
{
|
||||
Write-Error "$WinAppDriverHash"
|
||||
throw "WindowsApplicationDriver_1.2.1.msi has unexpected SHA256 hash: $Hash"
|
||||
}
|
||||
|
||||
# Install WinAppDriver
|
||||
Start-Process msiexec.exe -Wait -ArgumentList "/I $($ENV:Temp)\WindowsApplicationDriver_1.2.1.msi /quiet /passive"
|
||||
@@ -33,4 +33,7 @@ jobs:
|
||||
platform: x64
|
||||
- template: ./templates/build-powertoys-ci.yml
|
||||
parameters:
|
||||
platform: arm64
|
||||
platform: arm64
|
||||
- template: ./templates/run-ui-tests-ci.yml
|
||||
parameters:
|
||||
platform: x64
|
||||
|
||||
@@ -91,21 +91,21 @@ steps:
|
||||
displayName: 'nuget restore packages.config'
|
||||
|
||||
- task: VSBuild@1
|
||||
displayName: 'Build PowerToys.sln'
|
||||
displayName: 'Build and Test PowerToys.sln'
|
||||
inputs:
|
||||
solution: '**\PowerToys.sln'
|
||||
vsVersion: 17.0
|
||||
platform: '$(BuildPlatform)'
|
||||
configuration: '$(BuildConfiguration)'
|
||||
${{ if eq(parameters.enableCaching, true) }}:
|
||||
msbuildArgs: -restore ${{ parameters.additionalBuildArguments }} -graph -reportfileaccesses -p:MSBuildCacheEnabled=true -p:MSBuildCacheLogDirectory=$(Build.ArtifactStagingDirectory)\logs\MSBuildCache -bl:$(Build.ArtifactStagingDirectory)\logs\PowerToys.binlog -ds:false
|
||||
msbuildArgs: -restore ${{ parameters.additionalBuildArguments }} -t:Build;Test -graph -reportfileaccesses -p:MSBuildCacheEnabled=true -p:MSBuildCacheLogDirectory=$(Build.ArtifactStagingDirectory)\logs\MSBuildCache -bl:$(Build.ArtifactStagingDirectory)\logs\PowerToys.binlog -ds:false
|
||||
${{ else }}:
|
||||
msbuildArgs: -restore ${{ parameters.additionalBuildArguments }} -bl:$(Build.ArtifactStagingDirectory)\logs\PowerToys.binlog -ds:false
|
||||
msbuildArgs: -restore ${{ parameters.additionalBuildArguments }} -t:Build;Test -graph -bl:$(Build.ArtifactStagingDirectory)\logs\PowerToys.binlog -ds:false
|
||||
msbuildArchitecture: x64
|
||||
maximumCpuCount: true
|
||||
${{ if eq(parameters.enableCaching, true) }}:
|
||||
env:
|
||||
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
||||
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
||||
|
||||
- task: VSBuild@1
|
||||
displayName: 'Build BugReportTool.sln'
|
||||
@@ -114,7 +114,7 @@ steps:
|
||||
vsVersion: 17.0
|
||||
platform: '$(BuildPlatform)'
|
||||
configuration: '$(BuildConfiguration)'
|
||||
msbuildArgs: -restore ${{ parameters.additionalBuildArguments }} -bl:$(Build.ArtifactStagingDirectory)\logs\BugReportTool.binlog -ds:false
|
||||
msbuildArgs: -restore ${{ parameters.additionalBuildArguments }} -graph -bl:$(Build.ArtifactStagingDirectory)\logs\BugReportTool.binlog -ds:false
|
||||
msbuildArchitecture: x64
|
||||
maximumCpuCount: true
|
||||
|
||||
@@ -125,7 +125,7 @@ steps:
|
||||
vsVersion: 17.0
|
||||
platform: '$(BuildPlatform)'
|
||||
configuration: '$(BuildConfiguration)'
|
||||
msbuildArgs: -restore ${{ parameters.additionalBuildArguments }} -bl:$(Build.ArtifactStagingDirectory)\logs\WebcamReportTool.binlog -ds:false
|
||||
msbuildArgs: -restore ${{ parameters.additionalBuildArguments }} -graph -bl:$(Build.ArtifactStagingDirectory)\logs\WebcamReportTool.binlog -ds:false
|
||||
msbuildArchitecture: x64
|
||||
maximumCpuCount: true
|
||||
|
||||
@@ -136,7 +136,7 @@ steps:
|
||||
vsVersion: 17.0
|
||||
platform: '$(BuildPlatform)'
|
||||
configuration: '$(BuildConfiguration)'
|
||||
msbuildArgs: -restore ${{ parameters.additionalBuildArguments }} -bl:$(Build.ArtifactStagingDirectory)\logs\StylesReportTool.binlog -ds:false
|
||||
msbuildArgs: -restore ${{ parameters.additionalBuildArguments }} -graph -bl:$(Build.ArtifactStagingDirectory)\logs\StylesReportTool.binlog -ds:false
|
||||
msbuildArchitecture: x64
|
||||
maximumCpuCount: true
|
||||
|
||||
@@ -222,47 +222,13 @@ steps:
|
||||
arguments: -targetDir '$(build.sourcesdirectory)\$(BuildPlatform)\$(BuildConfiguration)\WinUI3Apps'
|
||||
pwsh: true
|
||||
|
||||
# directly not doing WinAppDriver testing
|
||||
- task: VSTest@2
|
||||
displayName: 'MS Tests'
|
||||
condition: ne(variables['BuildPlatform'], 'arm64') # No arm64 agents to run the tests.
|
||||
# Publish test results which ran in MSBuild
|
||||
- task: PublishTestResults@2
|
||||
displayName: 'Publish Test Results'
|
||||
inputs:
|
||||
platform: '$(BuildPlatform)'
|
||||
configuration: '$(BuildConfiguration)'
|
||||
testSelector: 'testAssemblies'
|
||||
testAssemblyVer2: |
|
||||
**\UnitTests-GcodeThumbnailProvider.dll
|
||||
**\UnitTests-StlThumbnailProvider.dll
|
||||
**\UnitTests-PdfThumbnailProvider.dll
|
||||
**\UnitTests-QoiThumbnailProvider.dll
|
||||
**\Settings.UI.UnitTests.dll
|
||||
**\UnitTests-GcodePreviewHandler.dll
|
||||
**\UnitTests-QoiPreviewHandler.dll
|
||||
**\UnitTests-FancyZonesEditor.dll
|
||||
**\UnitTests-PdfPreviewHandler.dll
|
||||
**\UnitTests-PreviewHandlerCommon.dll
|
||||
**\Microsoft.PowerToys.Run.Plugin.Registry.UnitTests.dll
|
||||
**\UnitTest-ColorPickerUI.dll
|
||||
**\Microsoft.Interop.Tests.dll
|
||||
**\ImageResizer.Test.dll
|
||||
**\Community.PowerToys.Run.Plugin.UnitConverter.UnitTest.dll
|
||||
**\Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests.dll
|
||||
**\Microsoft.Plugin.Folder.UnitTests.dll
|
||||
**\Microsoft.Plugin.Program.UnitTests.dll
|
||||
**\Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest.dll
|
||||
**\Microsoft.Plugin.Uri.UnitTests.dll
|
||||
**\Wox.Test.dll
|
||||
**\Microsoft.PowerToys.Run.Plugin.System.UnitTests.dll
|
||||
**\Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests.dll
|
||||
**\Microsoft.Plugin.WindowsTerminal.UnitTests.dll
|
||||
**\Microsoft.Plugin.WindowWalker.UnitTests.dll
|
||||
**\PreviewPaneUnitTests.dll
|
||||
**\UnitTests-SvgThumbnailProvider.dll
|
||||
**\UnitTests-SvgPreviewHandler.dll
|
||||
**\PowerToys.Hosts.Tests.dll
|
||||
**\MouseJumpUI.UnitTests.dll
|
||||
!**\obj\**
|
||||
!**\ref\**
|
||||
testResultsFormat: VSTest
|
||||
testResultsFiles: '**/*.trx'
|
||||
condition: always()
|
||||
|
||||
# Native dlls
|
||||
- task: VSTest@2
|
||||
@@ -277,7 +243,6 @@ steps:
|
||||
**\KeyboardManagerEditorTest.dll
|
||||
**\UnitTests-CommonLib.dll
|
||||
**\PowerRenameUnitTests.dll
|
||||
**\powerpreviewTest.dll
|
||||
**\UnitTests-FancyZones.dll
|
||||
!**\obj\**
|
||||
|
||||
@@ -299,3 +264,16 @@ steps:
|
||||
displayName: Publish Logs
|
||||
artifact: '$(System.JobDisplayName) logs'
|
||||
condition: always()
|
||||
|
||||
- task: CopyFiles@2
|
||||
displayName: Copy Build Files
|
||||
condition: and(succeeded(), ne(variables['BuildPlatform'],'arm64'))
|
||||
inputs:
|
||||
sourceFolder: '$(Build.SourcesDirectory)'
|
||||
contents: '$(BuildPlatform)/$(BuildConfiguration)/**/*'
|
||||
targetFolder: '$(Build.ArtifactStagingDirectory)\$(BuildPlatform)\$(BuildConfiguration)'
|
||||
|
||||
- publish: $(Build.ArtifactStagingDirectory)\$(BuildPlatform)\$(BuildConfiguration)
|
||||
displayName: Publish Build Artifacts
|
||||
artifact: build-$(BuildPlatform)-$(BuildConfiguration)
|
||||
condition: and(succeeded(), ne(variables['BuildPlatform'],'arm64'))
|
||||
|
||||
70
.pipelines/ci/templates/run-ui-tests-ci.yml
Normal file
@@ -0,0 +1,70 @@
|
||||
parameters:
|
||||
configuration: 'Release'
|
||||
platform: ''
|
||||
|
||||
jobs:
|
||||
- job: UITest
|
||||
displayName: UI Test ${{ parameters.platform }} ${{ parameters.configuration }}
|
||||
dependsOn: Build${{ parameters.platform }}${{ parameters.configuration }}
|
||||
variables:
|
||||
SrcPath: $(Build.Repository.LocalPath)
|
||||
pool:
|
||||
${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: SHINE-OSS-Testing-x64
|
||||
${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: SHINE-INT-Testing-x64
|
||||
steps:
|
||||
- checkout: self
|
||||
fetchDepth: 1
|
||||
submodules: false
|
||||
clean: true
|
||||
fetchTags: false
|
||||
|
||||
- download: current
|
||||
displayName: Download artifacts
|
||||
artifact: build-${{ parameters.platform }}-${{ parameters.configuration }}
|
||||
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Use .NET 6 SDK'
|
||||
inputs:
|
||||
packageType: sdk
|
||||
version: '6.x'
|
||||
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Use .NET 8 SDK'
|
||||
inputs:
|
||||
packageType: sdk
|
||||
version: '8.x'
|
||||
includePreviewVersions: true
|
||||
|
||||
- task: VisualStudioTestPlatformInstaller@1
|
||||
displayName: Ensure VSTest Platform
|
||||
|
||||
- task: PowerShell@2
|
||||
displayName: Download and install WinAppDriver
|
||||
inputs:
|
||||
targetType: filePath
|
||||
filePath: '$(build.sourcesdirectory)\.pipelines\InstallWinAppDriver.ps1'
|
||||
|
||||
- task: ScreenResolutionUtility@1
|
||||
inputs:
|
||||
displaySettings: 'optimal'
|
||||
|
||||
- task: VSTest@2
|
||||
displayName: 'UI Tests'
|
||||
condition: and(succeeded(), ne(variables['BuildPlatform'],'arm64')) # No arm64 agents to run the tests.
|
||||
inputs:
|
||||
platform: '$(BuildPlatform)'
|
||||
configuration: '$(BuildConfiguration)'
|
||||
testSelector: 'testAssemblies'
|
||||
searchFolder: '$(Pipeline.Workspace)\build-${{ parameters.platform }}-${{ parameters.configuration }}'
|
||||
vstestLocationMethod: 'location' # otherwise fails to find vstest.console.exe
|
||||
#vstestLocation: '$(Agent.ToolsDirectory)\VsTest\**\${{ parameters.platform }}\tools\net462\Common7\IDE\Extensions\TestPlatform'
|
||||
vstestLocation: '$(Agent.ToolsDirectory)\VsTest\17.10.0-release-24177-07\x64\tools\net462\Common7\IDE\Extensions\TestPlatform'
|
||||
uiTests: true
|
||||
rerunFailedTests: true
|
||||
testAssemblyVer2: |
|
||||
**\UITests-FancyZones.dll
|
||||
**\UITests-FancyZonesEditor.dll
|
||||
!**\obj\**
|
||||
!**\ref\**
|
||||
@@ -1,19 +1,19 @@
|
||||
$ProgressPreference = 'SilentlyContinue'
|
||||
|
||||
$WixDownloadUrl = "https://github.com/wixtoolset/wix3/releases/download/wix314rtm/wix314.exe"
|
||||
$WixBinariesDownloadUrl = "https://github.com/wixtoolset/wix3/releases/download/wix314rtm/wix314-binaries.zip"
|
||||
$WixDownloadUrl = "https://github.com/wixtoolset/wix3/releases/download/wix3141rtm/wix314.exe"
|
||||
$WixBinariesDownloadUrl = "https://github.com/wixtoolset/wix3/releases/download/wix3141rtm/wix314-binaries.zip"
|
||||
|
||||
# Download WiX binaries and verify their hash sums
|
||||
Invoke-WebRequest -Uri $WixDownloadUrl -OutFile "$($ENV:Temp)\wix314.exe"
|
||||
$Hash = (Get-FileHash -Algorithm SHA256 "$($ENV:Temp)\wix314.exe").Hash
|
||||
if ($Hash -ne '704439EA88FC9E5A3647EEDEEB45943F9A392E3D209F58512280130096847937')
|
||||
if ($Hash -ne '6BF6D03D6923D9EF827AE1D943B90B42B8EBB1B0F68EF6D55F868FA34C738A29')
|
||||
{
|
||||
Write-Error "$WixHash"
|
||||
throw "wix314.exe has unexpected SHA256 hash: $Hash"
|
||||
}
|
||||
Invoke-WebRequest -Uri $WixBinariesDownloadUrl -OutFile "$($ENV:Temp)\wix314-binaries.zip"
|
||||
$Hash = (Get-FileHash -Algorithm SHA256 "$($ENV:Temp)\wix314-binaries.zip").Hash
|
||||
if($Hash -ne '13F067F38969FAF163D93A804B48EA0576790A202C8F10291F2000F0E356E934')
|
||||
if($Hash -ne '6AC824E1642D6F7277D0ED7EA09411A508F6116BA6FAE0AA5F2C7DAA2FF43D31')
|
||||
{
|
||||
throw "wix314-binaries.zip has unexpected SHA256 hash: $Hash"
|
||||
}
|
||||
|
||||
@@ -43,10 +43,6 @@ steps:
|
||||
- powershell: 'tar czf LocOutput.tar.gz LocOutput'
|
||||
displayName: 'PowerShell Script'
|
||||
|
||||
- task: PublishBuildArtifacts@1
|
||||
- publish: LocOutput.tar.gz
|
||||
displayName: 'Publish Artifact: LocOutput'
|
||||
inputs:
|
||||
PathtoPublish: LocOutput.tar.gz
|
||||
ArtifactName: LocOutput
|
||||
|
||||
|
||||
artifact: LocOutput
|
||||
|
||||
@@ -156,7 +156,7 @@ extends:
|
||||
inputs:
|
||||
solution: '**\PowerToys.sln'
|
||||
vsVersion: 17.0
|
||||
msbuildArgs: -restore /p:RestorePackagesConfig=true /p:RestoreConfigFile="$(Build.SourcesDirectory)\.pipelines\release-nuget.config" /p:CIBuild=true /bl:$(Build.SourcesDirectory)\msbuild.binlog
|
||||
msbuildArgs: -restore -graph /p:RestorePackagesConfig=true /p:RestoreConfigFile="$(Build.SourcesDirectory)\.pipelines\release-nuget.config" /p:CIBuild=true /bl:$(Build.SourcesDirectory)\msbuild.binlog
|
||||
platform: $(BuildPlatform)
|
||||
configuration: $(BuildConfiguration)
|
||||
clean: true
|
||||
@@ -167,7 +167,7 @@ extends:
|
||||
inputs:
|
||||
solution: '**/tools/BugReportTool/BugReportTool.sln'
|
||||
vsVersion: 17.0
|
||||
msbuildArgs: -restore /p:RestorePackagesConfig=true /p:RestoreConfigFile="$(Build.SourcesDirectory)\.pipelines\release-nuget.config" /p:CIBuild=true /bl:$(Build.SourcesDirectory)\msbuild.binlog
|
||||
msbuildArgs: -restore -graph /p:RestorePackagesConfig=true /p:RestoreConfigFile="$(Build.SourcesDirectory)\.pipelines\release-nuget.config" /p:CIBuild=true /bl:$(Build.SourcesDirectory)\msbuild.binlog
|
||||
platform: $(BuildPlatform)
|
||||
configuration: $(BuildConfiguration)
|
||||
clean: true
|
||||
@@ -178,7 +178,7 @@ extends:
|
||||
inputs:
|
||||
solution: '**/tools/WebcamReportTool/WebcamReportTool.sln'
|
||||
vsVersion: 17.0
|
||||
msbuildArgs: -restore /p:RestorePackagesConfig=true /p:RestoreConfigFile="$(Build.SourcesDirectory)\.pipelines\release-nuget.config" /p:CIBuild=true /bl:$(Build.SourcesDirectory)\msbuild.binlog
|
||||
msbuildArgs: -restore -graph /p:RestorePackagesConfig=true /p:RestoreConfigFile="$(Build.SourcesDirectory)\.pipelines\release-nuget.config" /p:CIBuild=true /bl:$(Build.SourcesDirectory)\msbuild.binlog
|
||||
platform: $(BuildPlatform)
|
||||
configuration: $(BuildConfiguration)
|
||||
clean: true
|
||||
@@ -189,7 +189,7 @@ extends:
|
||||
inputs:
|
||||
solution: '**/tools/StylesReportTool/StylesReportTool.sln'
|
||||
vsVersion: 17.0
|
||||
msbuildArgs: -restore /p:RestorePackagesConfig=true /p:RestoreConfigFile="$(Build.SourcesDirectory)\.pipelines\release-nuget.config" /p:CIBuild=true /bl:$(Build.SourcesDirectory)\msbuild.binlog
|
||||
msbuildArgs: -restore -graph /p:RestorePackagesConfig=true /p:RestoreConfigFile="$(Build.SourcesDirectory)\.pipelines\release-nuget.config" /p:CIBuild=true /bl:$(Build.SourcesDirectory)\msbuild.binlog
|
||||
platform: $(BuildPlatform)
|
||||
configuration: $(BuildConfiguration)
|
||||
clean: true
|
||||
@@ -202,6 +202,7 @@ extends:
|
||||
vsVersion: 17.0
|
||||
msbuildArgs: >-
|
||||
/target:Publish
|
||||
/graph
|
||||
/p:Configuration=$(BuildConfiguration);Platform=$(BuildPlatform);AppxBundle=Never
|
||||
/p:VCRTForwarders-IncludeDebugCRT=false
|
||||
/p:PowerToysRoot=$(Build.SourcesDirectory)
|
||||
@@ -218,6 +219,7 @@ extends:
|
||||
# The arguments should be the same as the ones for Settings; make sure they are.
|
||||
msbuildArgs: >-
|
||||
/target:Publish
|
||||
/graph
|
||||
/p:Configuration=$(BuildConfiguration);Platform=$(BuildPlatform);AppxBundle=Never
|
||||
/p:VCRTForwarders-IncludeDebugCRT=false
|
||||
/p:PowerToysRoot=$(Build.SourcesDirectory)
|
||||
@@ -234,6 +236,7 @@ extends:
|
||||
# The arguments should be the same as the ones for Settings; make sure they are.
|
||||
msbuildArgs: >-
|
||||
/target:Publish
|
||||
/graph
|
||||
/p:Configuration=$(BuildConfiguration);Platform=$(BuildPlatform);AppxBundle=Never
|
||||
/p:VCRTForwarders-IncludeDebugCRT=false
|
||||
/p:PowerToysRoot=$(Build.SourcesDirectory)
|
||||
@@ -250,6 +253,7 @@ extends:
|
||||
# The arguments should be the same as the ones for Settings; make sure they are.
|
||||
msbuildArgs: >-
|
||||
/target:Publish
|
||||
/graph
|
||||
/p:Configuration=$(BuildConfiguration);Platform=$(BuildPlatform);AppxBundle=Never
|
||||
/p:VCRTForwarders-IncludeDebugCRT=false
|
||||
/p:PowerToysRoot=$(Build.SourcesDirectory)
|
||||
@@ -266,6 +270,7 @@ extends:
|
||||
# The arguments should be the same as the ones for Settings; make sure they are.
|
||||
msbuildArgs: >-
|
||||
/target:Publish
|
||||
/graph
|
||||
/p:Configuration=$(BuildConfiguration);Platform=$(BuildPlatform);AppxBundle=Never
|
||||
/p:VCRTForwarders-IncludeDebugCRT=false
|
||||
/p:PowerToysRoot=$(Build.SourcesDirectory)
|
||||
@@ -282,6 +287,7 @@ extends:
|
||||
# The arguments should be the same as the ones for Settings; make sure they are.
|
||||
msbuildArgs: >-
|
||||
/target:Publish
|
||||
/graph
|
||||
/p:Configuration=$(BuildConfiguration);Platform=$(BuildPlatform);AppxBundle=Never
|
||||
/p:VCRTForwarders-IncludeDebugCRT=false
|
||||
/p:PowerToysRoot=$(Build.SourcesDirectory)
|
||||
@@ -298,6 +304,7 @@ extends:
|
||||
# The arguments should be the same as the ones for Settings; make sure they are.
|
||||
msbuildArgs: >-
|
||||
/target:Publish
|
||||
/graph
|
||||
/p:Configuration=$(BuildConfiguration);Platform=$(BuildPlatform);AppxBundle=Never
|
||||
/p:VCRTForwarders-IncludeDebugCRT=false
|
||||
/p:PowerToysRoot=$(Build.SourcesDirectory)
|
||||
@@ -342,6 +349,15 @@ extends:
|
||||
batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_core.json'
|
||||
ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml'
|
||||
|
||||
- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@3
|
||||
displayName: Sign DSC Powershell files
|
||||
inputs:
|
||||
ConnectedServiceName: 'Terminal/Console/WinAppDriver Team Code Signing Connection'
|
||||
FolderPath: 'src/dsc/Microsoft.PowerToys.Configure'
|
||||
signType: batchSigning
|
||||
batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_DSC.json'
|
||||
ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml'
|
||||
|
||||
- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@3
|
||||
displayName: Sign x86 directshow VCM
|
||||
inputs:
|
||||
|
||||
@@ -15,7 +15,8 @@ Param(
|
||||
$referencedFileVersionsPerDll = @{}
|
||||
$totalFailures = 0
|
||||
|
||||
Get-ChildItem $targetDir -Recurse -Filter *.deps.json | ForEach-Object {
|
||||
Get-ChildItem $targetDir -Recurse -Filter *.deps.json -Exclude UITests-FancyZones* | ForEach-Object {
|
||||
# Temporarily exclude FancyZones UI tests because of Appium.WebDriver dependencies
|
||||
$depsJsonFullFileName = $_.FullName
|
||||
$depsJsonFileName = $_.Name
|
||||
$depsJson = Get-Content $depsJsonFullFileName | ConvertFrom-Json
|
||||
|
||||
@@ -44,6 +44,20 @@
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Add ability to run tests via "msbuild /t:Test" -->
|
||||
<Sdk Name="Microsoft.Build.RunVSTest" Version="1.0.319" />
|
||||
<PropertyGroup>
|
||||
<VSTestLogger>trx</VSTestLogger>
|
||||
<!--
|
||||
RunVSTest by default uses %VSINSTALLDIR%\Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe,
|
||||
but some of the CI scenarios don't define %VSINSTALLDIR%, so be explicit about where to look for vstest.
|
||||
Note: $(VsInstallRoot) is a built-in MSBuild property, so should always be defined.
|
||||
-->
|
||||
<VSTestToolPath>$(VsInstallRoot)\Common7\IDE\CommonExtensions\Microsoft\TestWindow</VSTestToolPath>
|
||||
<!-- No arm64 agents to run the tests. -->
|
||||
<RunVSTest Condition="'$(Platform)' == 'ARM64'">false</RunVSTest>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- MSBuildCache -->
|
||||
<PropertyGroup>
|
||||
<!-- Off by default -->
|
||||
@@ -72,6 +86,13 @@
|
||||
$(LocalAppData)\Microsoft\Windows\INetCache\**;
|
||||
A:\;
|
||||
E:\;
|
||||
$(windir)\**;
|
||||
</MSBuildCacheAllowFileAccessAfterProjectFinishFilePatterns>
|
||||
|
||||
<!-- Unit tests of low-priv processes, eg the preview handler tests, may log to this location. -->
|
||||
<MSBuildCacheAllowFileAccessAfterProjectFinishFilePatterns>
|
||||
$(MSBuildCacheAllowFileAccessAfterProjectFinishFilePatterns);
|
||||
$(USERPROFILE)\AppData\LocalLow\Microsoft\PowerToys\**;
|
||||
</MSBuildCacheAllowFileAccessAfterProjectFinishFilePatterns>
|
||||
|
||||
<!--
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="Appium.WebDriver" Version="4.2.1" />
|
||||
<PackageVersion Include="Appium.WebDriver" Version="4.4.5" />
|
||||
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.2.2" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Animations" Version="8.0.240109" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Collections" Version="8.0.240109" />
|
||||
@@ -73,7 +73,7 @@
|
||||
<PackageVersion Include="System.Diagnostics.EventLog" Version="8.0.0" />
|
||||
<!-- Package System.Diagnostics.PerformanceCounter added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Data.OleDb but the 8.0.1 version wasn't published to nuget. -->
|
||||
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="8.0.0" />
|
||||
<PackageVersion Include="System.Drawing.Common" Version="8.0.3" />
|
||||
<PackageVersion Include="System.Drawing.Common" Version="8.0.4" />
|
||||
<PackageVersion Include="System.IO.Abstractions" Version="17.2.3" />
|
||||
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="17.2.3" />
|
||||
<PackageVersion Include="System.Management" Version="8.0.0" />
|
||||
|
||||
@@ -1296,6 +1296,7 @@ EXHIBIT A -Mozilla Public License.
|
||||
|
||||
## NuGet Packages used by PowerToys
|
||||
|
||||
- Appium.WebDriver 4.4.5
|
||||
- CommunityToolkit.Mvvm 8.2.2
|
||||
- CommunityToolkit.WinUI.Animations 8.0.240109
|
||||
- CommunityToolkit.WinUI.Collections 8.0.240109
|
||||
@@ -1353,7 +1354,7 @@ EXHIBIT A -Mozilla Public License.
|
||||
- System.Data.SqlClient 4.8.6
|
||||
- System.Diagnostics.EventLog 8.0.0
|
||||
- System.Diagnostics.PerformanceCounter 8.0.0
|
||||
- System.Drawing.Common 8.0.3
|
||||
- System.Drawing.Common 8.0.4
|
||||
- System.IO.Abstractions 17.2.3
|
||||
- System.IO.Abstractions.TestingHelpers 17.2.3
|
||||
- System.Management 8.0.0
|
||||
|
||||
@@ -568,6 +568,19 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FileLocksmithContextMenu",
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FileLocksmithLib", "src\modules\FileLocksmith\FileLocksmithLib\FileLocksmithLib.vcxproj", "{9D52FD25-EF90-4F9A-A015-91EFC5DAF54F}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UITests-FancyZones", "src\modules\fancyzones\UITests-FancyZones\UITests-FancyZones.csproj", "{FE38FC07-1C05-4B57-ADA3-2FE2F53C6A52}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UITests-FancyZonesEditor", "src\modules\fancyzones\UITests-FancyZonesEditor\UITests-FancyZonesEditor.csproj", "{3A9A791E-94A9-49F8-8401-C11CE288D5FB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FancyZonesEditorCommon", "src\modules\fancyzones\FancyZonesEditorCommon\FancyZonesEditorCommon.csproj", "{C0974915-8A1D-4BF0-977B-9587D3807AB7}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DSC", "DSC", "{557C4636-D7E1-4838-A504-7D19B725EE95}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerToys.Settings.DSC.Schema.Generator", "src\dsc\PowerToys.Settings.DSC.Schema.Generator\PowerToys.Settings.DSC.Schema.Generator.csproj", "{1D6893CB-BC0C-46A8-A76C-9728706CA51A}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{020A7474-3601-4160-A159-D7B70B77B15F} = {020A7474-3601-4160-A159-D7B70B77B15F}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|ARM64 = Debug|ARM64
|
||||
@@ -2494,6 +2507,54 @@ Global
|
||||
{9D52FD25-EF90-4F9A-A015-91EFC5DAF54F}.Release|x64.Build.0 = Release|x64
|
||||
{9D52FD25-EF90-4F9A-A015-91EFC5DAF54F}.Release|x86.ActiveCfg = Release|x64
|
||||
{9D52FD25-EF90-4F9A-A015-91EFC5DAF54F}.Release|x86.Build.0 = Release|x64
|
||||
{FE38FC07-1C05-4B57-ADA3-2FE2F53C6A52}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{FE38FC07-1C05-4B57-ADA3-2FE2F53C6A52}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{FE38FC07-1C05-4B57-ADA3-2FE2F53C6A52}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{FE38FC07-1C05-4B57-ADA3-2FE2F53C6A52}.Debug|x64.Build.0 = Debug|x64
|
||||
{FE38FC07-1C05-4B57-ADA3-2FE2F53C6A52}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{FE38FC07-1C05-4B57-ADA3-2FE2F53C6A52}.Debug|x86.Build.0 = Debug|x64
|
||||
{FE38FC07-1C05-4B57-ADA3-2FE2F53C6A52}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{FE38FC07-1C05-4B57-ADA3-2FE2F53C6A52}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{FE38FC07-1C05-4B57-ADA3-2FE2F53C6A52}.Release|x64.ActiveCfg = Release|x64
|
||||
{FE38FC07-1C05-4B57-ADA3-2FE2F53C6A52}.Release|x64.Build.0 = Release|x64
|
||||
{FE38FC07-1C05-4B57-ADA3-2FE2F53C6A52}.Release|x86.ActiveCfg = Release|x64
|
||||
{FE38FC07-1C05-4B57-ADA3-2FE2F53C6A52}.Release|x86.Build.0 = Release|x64
|
||||
{3A9A791E-94A9-49F8-8401-C11CE288D5FB}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{3A9A791E-94A9-49F8-8401-C11CE288D5FB}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{3A9A791E-94A9-49F8-8401-C11CE288D5FB}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{3A9A791E-94A9-49F8-8401-C11CE288D5FB}.Debug|x64.Build.0 = Debug|x64
|
||||
{3A9A791E-94A9-49F8-8401-C11CE288D5FB}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{3A9A791E-94A9-49F8-8401-C11CE288D5FB}.Debug|x86.Build.0 = Debug|x64
|
||||
{3A9A791E-94A9-49F8-8401-C11CE288D5FB}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{3A9A791E-94A9-49F8-8401-C11CE288D5FB}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{3A9A791E-94A9-49F8-8401-C11CE288D5FB}.Release|x64.ActiveCfg = Release|x64
|
||||
{3A9A791E-94A9-49F8-8401-C11CE288D5FB}.Release|x64.Build.0 = Release|x64
|
||||
{3A9A791E-94A9-49F8-8401-C11CE288D5FB}.Release|x86.ActiveCfg = Release|x64
|
||||
{3A9A791E-94A9-49F8-8401-C11CE288D5FB}.Release|x86.Build.0 = Release|x64
|
||||
{C0974915-8A1D-4BF0-977B-9587D3807AB7}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{C0974915-8A1D-4BF0-977B-9587D3807AB7}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{C0974915-8A1D-4BF0-977B-9587D3807AB7}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{C0974915-8A1D-4BF0-977B-9587D3807AB7}.Debug|x64.Build.0 = Debug|x64
|
||||
{C0974915-8A1D-4BF0-977B-9587D3807AB7}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{C0974915-8A1D-4BF0-977B-9587D3807AB7}.Debug|x86.Build.0 = Debug|x64
|
||||
{C0974915-8A1D-4BF0-977B-9587D3807AB7}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{C0974915-8A1D-4BF0-977B-9587D3807AB7}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{C0974915-8A1D-4BF0-977B-9587D3807AB7}.Release|x64.ActiveCfg = Release|x64
|
||||
{C0974915-8A1D-4BF0-977B-9587D3807AB7}.Release|x64.Build.0 = Release|x64
|
||||
{C0974915-8A1D-4BF0-977B-9587D3807AB7}.Release|x86.ActiveCfg = Release|x64
|
||||
{C0974915-8A1D-4BF0-977B-9587D3807AB7}.Release|x86.Build.0 = Release|x64
|
||||
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Debug|x64.Build.0 = Debug|x64
|
||||
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Debug|x86.Build.0 = Debug|x64
|
||||
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Release|x64.ActiveCfg = Release|x64
|
||||
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Release|x64.Build.0 = Release|x64
|
||||
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Release|x86.ActiveCfg = Release|x64
|
||||
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Release|x86.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -2703,6 +2764,10 @@ Global
|
||||
{0014D652-901F-4456-8D65-06FC5F997FB0} = {4C0D0746-BE5B-49EE-BD5D-A7811628AE8B}
|
||||
{799A50D8-DE89-4ED1-8FF8-AD5A9ED8C0CA} = {AB82E5DD-C32D-4F28-9746-2C780846188E}
|
||||
{9D52FD25-EF90-4F9A-A015-91EFC5DAF54F} = {AB82E5DD-C32D-4F28-9746-2C780846188E}
|
||||
{FE38FC07-1C05-4B57-ADA3-2FE2F53C6A52} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD}
|
||||
{3A9A791E-94A9-49F8-8401-C11CE288D5FB} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD}
|
||||
{C0974915-8A1D-4BF0-977B-9587D3807AB7} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD}
|
||||
{1D6893CB-BC0C-46A8-A76C-9728706CA51A} = {557C4636-D7E1-4838-A504-7D19B725EE95}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
|
||||
|
||||
172
README.md
@@ -40,19 +40,19 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline
|
||||
Go to the [Microsoft PowerToys GitHub releases page][github-release-link] and click on `Assets` at the bottom to show the files available in the release. Please use the appropriate PowerToys installer that matches your machine's architecture and install scope. For most, it is `x64` and per-user.
|
||||
|
||||
<!-- items that need to be updated release to release -->
|
||||
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=project%3Amicrosoft%2FPowerToys%2F53
|
||||
[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=project%3Amicrosoft%2FPowerToys%2F52
|
||||
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.79.0/PowerToysUserSetup-0.79.0-x64.exe
|
||||
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.79.0/PowerToysUserSetup-0.79.0-arm64.exe
|
||||
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.79.0/PowerToysSetup-0.79.0-x64.exe
|
||||
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.79.0/PowerToysSetup-0.79.0-arm64.exe
|
||||
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=project%3Amicrosoft%2FPowerToys%2F54
|
||||
[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=project%3Amicrosoft%2FPowerToys%2F53
|
||||
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.80.0/PowerToysUserSetup-0.80.0-x64.exe
|
||||
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.80.0/PowerToysUserSetup-0.80.0-arm64.exe
|
||||
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.80.0/PowerToysSetup-0.80.0-x64.exe
|
||||
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.80.0/PowerToysSetup-0.80.0-arm64.exe
|
||||
|
||||
| Description | Filename | sha256 hash |
|
||||
|----------------|----------|-------------|
|
||||
| Per user - x64 | [PowerToysUserSetup-0.79.0-x64.exe][ptUserX64] | CF1C715F952A34416CDBE5D06D24FFF47790DDA1D4CA3F81BCAD9D28FF0039A1 |
|
||||
| Per user - ARM64 | [PowerToysUserSetup-0.79.0-arm64.exe][ptUserArm64] | ADE572B6F1B59DCDC60A2550D9FD00B8CC7C78BE9330F534691CE4B056ED76F1 |
|
||||
| Machine wide - x64 | [PowerToysSetup-0.79.0-x64.exe][ptMachineX64] | 3FD2A6BD9C8F8973BFBBF5DB9236C3D8AF3AE57E5AEC275DDEB5EF31581F80FE |
|
||||
| Machine wide - ARM64 | [PowerToysSetup-0.79.0-arm64.exe][ptMachineArm64] | B93017C2A5CFB0DEF708DB412570AA39828E91D85E800EFD22481B46F0DC6852 |
|
||||
| Per user - x64 | [PowerToysUserSetup-0.80.0-x64.exe][ptUserX64] | 4D20EB01C4035BB41F57D43AED2A546547E1FAA660FE29DC1CC699F1916DE1CC |
|
||||
| Per user - ARM64 | [PowerToysUserSetup-0.80.0-arm64.exe][ptUserArm64] | 1B85E95B0EC7D8CE1EE51B987449DA9A36DAAA4C27DF8EE4796001848EA2CBD1 |
|
||||
| Machine wide - x64 | [PowerToysSetup-0.80.0-x64.exe][ptMachineX64] | 2D17C1920D970332D93449184D7C2470052686FD4B3EB8ED49EF6475D1D1D62F |
|
||||
| Machine wide - ARM64 | [PowerToysSetup-0.80.0-arm64.exe][ptMachineArm64] | 0DD40B7A31E35472688A55A8E1ECE58847EA423F3F19FD7B8C557F1271F73F24 |
|
||||
|
||||
This is our preferred method.
|
||||
|
||||
@@ -98,144 +98,136 @@ For guidance on developing for PowerToys, please read the [developer docs](/doc/
|
||||
|
||||
Our [prioritized roadmap][roadmap] of features and utilities that the core team is focusing on.
|
||||
|
||||
### 0.79 - February 2024 Update
|
||||
### 0.80 - March 2024 Update
|
||||
|
||||
In this release, we focused on stability and improvements.
|
||||
In this release, we focused on stability and improvements. The next release is planned to be released during [Microsoft Build 2024](https://build.microsoft.com/) (late May).
|
||||
|
||||
**Highlights**
|
||||
|
||||
- New feature: Keyboard Manager allows mapping shortcuts to start applications or opening URIs. Thanks [@jefflord](https://github.com/jefflord)!
|
||||
- New feature: Keyboard Manager allows shortcuts with chords. Thanks [@jefflord](https://github.com/jefflord)!
|
||||
- Modernized Color Picker with Fluent UX. Thanks [@niels9001](https://github.com/niels9001)!
|
||||
- Peek now is able to preview drives. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
- File Locksmith has now an entry in the Windows 11 tier 1 context menu.
|
||||
- New feature: Desired State Configuration support, allowing the use of winget configure for PowerToys. Check the [DSC documentation](https://aka.ms/powertoys-docs-dsc-configure) for more information.
|
||||
- The Windows App SDK dependency was updated to 1.5.1, fixing many underlying UI issues.
|
||||
- WebP/WebM files support was added to Peek. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
- Audio files support was added to Peek. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
- Automated UI testing for FancyZones Editor was added to CI.
|
||||
|
||||
### General
|
||||
|
||||
- Refactored code so that English is used as a fallback language when a localized resource cannot be found.
|
||||
- Added a Quick Access entry to access the flyout from PowerToys' tray icon right click menu. Thanks [@pekvasnovsky](https://github.com/pekvasnovsky)!
|
||||
- Added support for Desired State Configuration in PowerToys, allowing the use of winget configure to configure many settings.
|
||||
|
||||
### Awake
|
||||
|
||||
- The setting now reverts to "Keep using the current power plan" after Awake deactivates itself after any of the timed modes has expired.
|
||||
- Fix an issue causing the "Keep screen on" option to disable after Awake deactivated itself.
|
||||
|
||||
### Color Picker
|
||||
|
||||
- Now uses WPFUI and the UI was updated to follow Fluent UX principles. Thanks [@niels9001](https://github.com/niels9001)!
|
||||
- Added enable and disable telemetry to align it with the other utilities.
|
||||
- Fixed a UI issue causing the color picker modal to hide part of the color bar. Thanks [@TheChilledBuffalo](https://github.com/TheChilledBuffalo)!
|
||||
|
||||
### Command Not Found
|
||||
|
||||
- Added telemetry for when a module instance is created in PowerShell.
|
||||
- Now tries to find a preview version of PowerShell if no stable version is found.
|
||||
|
||||
### FancyZones
|
||||
|
||||
- Fixed a memory leak occurring on work area changes.
|
||||
- Fixed a crash loading the editor when there's a layout with an empty name in the configuration file.
|
||||
- Refactored layout internal data structures and common code to allow for automated testing.
|
||||
- The pressing of the shift key is now detected through raw input to fix an issue causing the shift key to be locked for some users.
|
||||
|
||||
### File Explorer add-ons
|
||||
|
||||
- Added support to the .ksh, .zsh, .bsh and .env file types to Monaco previewer. Thanks [@Aaron-Junker](https://github.com/Aaron-Junker)!
|
||||
- Re-enabled the RendererAppContainer feature in WebView2, since the associated crash has been fixed in the latest WebView2 releases.
|
||||
- Fixed a crash occurring in the Monaco previewer when a file being previewed isn't found by the code behind.
|
||||
- Fixed an issue in the Markdown previewer adding a leading space to code blocks. Thanks [@Aaron-Junker](https://github.com/Aaron-Junker)!
|
||||
- Fixed wrong location and scaling of preview results on screens with different DPIs.
|
||||
- Added better clean up code to thumbnail handlers to prevent locking files.
|
||||
|
||||
### File Locksmith
|
||||
|
||||
- Added as an entry in the Windows 11 tier 1 context menu.
|
||||
- Allow multiple lines to wrap when viewing the modal with selected file paths. Thanks [@sanidhyas3s](https://github.com/sanidhyas3s)!
|
||||
|
||||
### Hosts File Editor
|
||||
### Installer
|
||||
|
||||
- Tweaked filter button style to indicate if filters are applied.
|
||||
- Added an error indicator to each input field to indicate why a new entry can't be created.
|
||||
- Added an in-line delete button for each entry.
|
||||
|
||||
### Image Resizer
|
||||
|
||||
- Units and resize modes are now localized.
|
||||
- Tweaked and improved UI. Thanks [@Jay-o-Way](https://github.com/Jay-o-Way)!
|
||||
- Fixed the final directory name of the PowerToys Run VSCode Workspaces plugin in the installation directory to match the plugin name. Thanks [@zetaloop](https://github.com/zetaloop)!
|
||||
- Used more generic names for the bootstrap steps, so that "Installing PowerToys" is not shown when uninstalling.
|
||||
|
||||
### Keyboard Manager
|
||||
|
||||
- Added a feature that allows remapping a shortcut to starting an application. Thanks [@jefflord](https://github.com/jefflord)!
|
||||
- Added a feature that allows remapping a shortcut to open a URI. Thanks [@jefflord](https://github.com/jefflord)!
|
||||
- Added chords to shortcuts. Thanks [@jefflord](https://github.com/jefflord)!
|
||||
- Send telemetry about the key/shortcut to key/shortcut remappings that are set. This doesn't include remap to text, application or URI since those might contain personal information.
|
||||
- Added telemetry to send a daily event that at least a key/shortcut to key/shortcut remapping was used.
|
||||
- Tweaked and fixed the chords code to better follow conventions when trying to call the same chord multiple times.
|
||||
|
||||
### Mouse Without Borders
|
||||
- Fixed an issue causing the target path string to be corrupted when registering as a service.
|
||||
|
||||
### Paste as Plain Text
|
||||
|
||||
- Prevent the start menu from activating when the Windows key is part of the activation shortcut and is released sooner.
|
||||
- Fixed an issue that would clear out KBM mappings when certain numpad keys were used as the second key of a chord.
|
||||
- Added a comment in localization files so that translators won't translate "Text" as "SMS".
|
||||
|
||||
### Peek
|
||||
|
||||
- Fixed a title bar issue after maximizing Peek's window. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
- Fixed a crash when trying to use Peek in File Explorer alternatives.
|
||||
- Added a previewer for drives. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
- The folder previewer will now asynchronously calculate size, similar to the Properties screen in File Explorer. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
- Added support to the .ksh, .zsh, .bsh and .env file types to Monaco previewer. Thanks [@Aaron-Junker](https://github.com/Aaron-Junker)!
|
||||
|
||||
### PowerRename
|
||||
|
||||
- PowerRename context menu accelerator key readded.
|
||||
- Tweaked PowerRename apply button style. Thanks [@niels9001](https://github.com/niels9001)!
|
||||
- Added support to .WebP/.WebM files in the image/video previewer. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
- Added support for audio files. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
- Fixed an issue causing the open file button in the title bar to be un-clickable. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
- Fixed an issue when previewing a folder with a dot in the name that caused Peek to try to preview it as a file. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
|
||||
### PowerToys Run
|
||||
|
||||
- Fixed an issue causing win32 application icons to not appear correctly in the Programs plugin.
|
||||
- Unified phrasing in the plugin descriptions.
|
||||
- Fixed an issue causing the PowerToys Run plugin settings to be cleared with each upgrade.
|
||||
- Fixed an issue causing VSCodeWorkspaces plugin to not find WSL workspaces.
|
||||
- Fixed results tooltip closing fast. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
- Improved the Registry plugin tooltip spacing. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
- Allow pressing '=' to replace the query with the current result when using the calculator plugin. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
- Optimized the code that gathers results from the plugin to reduce CPU consumption.
|
||||
- Optimized memory usage in the Window Walker plugin.
|
||||
- Fixed crashes and improved error handling when saving json configuration files.
|
||||
- The Program plugin will now correctly get the icon for a newly installed packaged application. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
- Added a setting to the Windows Search plugin to exclude files and patterns from the results. Thanks [@HydroH](https://github.com/HydroH)!
|
||||
- Fixed an issue showing thumbnails caused by a hash collision between similar images.
|
||||
- Added the "checkbox and multiline text box" additional property type for plugins and improved multiline text handling. Thanks [@htcfreek](https://github.com/htcfreek)!
|
||||
|
||||
### Quick Accent
|
||||
|
||||
- Added support for the Slovenian character set. Thanks [@aklemen](https://github.com/aklemen)!
|
||||
- Added the Schwa character to the Italian character set. Thanks [@damantioworks](https://github.com/damantioworks)!
|
||||
|
||||
### Registry Preview
|
||||
|
||||
- Fixed a crash when closing the application and the editor's right click menu is opened.
|
||||
- Allow alternative valid names for the root keys. Thanks [@e-t-l](https://github.com/e-t-l)!
|
||||
- Fixed an issue causing many pick file windows to be opened simultaneously. Thanks [@randyrants](https://github.com/randyrants)!
|
||||
|
||||
### Screen Ruler
|
||||
|
||||
- Updated the measure icons for clarity. Thanks [@Aaron-Junker](https://github.com/Aaron-Junker) and [@niels9001](https://github.com/niels9001)!
|
||||
|
||||
### Shortcut Guide
|
||||
|
||||
- Updated the Emoji shortcut that is shown to the new Windows key + period (.) hotkey.
|
||||
|
||||
### Text Extractor
|
||||
|
||||
- Fixed issues creating the extract layout on certain monitor configurations.
|
||||
|
||||
### Video Conference Mute
|
||||
|
||||
- Added enable/disable telemetry to get usage data.
|
||||
|
||||
### Settings
|
||||
|
||||
- Fixed an alignment issue in the flyout icons causing some icons to be centered when they shouldn't. Thanks [@niels9001](https://github.com/niels9001)!
|
||||
- Added the mention that Monaco supports .txt files. Thanks [@Aaron-Junker](https://github.com/Aaron-Junker)!
|
||||
- Fixed an issue causing the Settings window to lose its previous maximized state. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
- Added locks to some terms (like the name of some utilities) so that they aren't localized.
|
||||
- Fixed some shortcuts not being shown properly in the Flyout and Dashboard. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
- Updated image for Color Picker and outdated animations for utilities in OOBE. Thanks [@niels9001](https://github.com/niels9001)!
|
||||
|
||||
### Documentation
|
||||
|
||||
- Fixed broken links in doc/devdocs/readme.md. Thanks [@jem-experience](https://github.com/jem-experience)!
|
||||
- Added FastWeb plugin to PowerToys Run thirdPartyRunPlugins.md docs. Thanks [@CCcat8059](https://github.com/CCcat8059)!
|
||||
- Removed the old security link to MSRC from the create new issue page, since security.md is already linked there.
|
||||
- Added clarity regarding unofficial plugins to the PowerToys Run thirdPartyRunPlugins.md docs.
|
||||
|
||||
### Development
|
||||
|
||||
- Updated Microsoft.MSBuildCache to 0.1.258-preview. Thanks [@dfederm](https://github.com/dfederm)!
|
||||
- Fixed CI to point VCToolsVersion to VC.CRT instead of VC.Redist version. Thanks [@snickler](https://github.com/snickler)!
|
||||
- Updated MSTest adapter and framework to 3.2.
|
||||
- Fixed CI by pointing WiX 3.14 urls and hashes to the latest release on GitHub.
|
||||
- Added Pro and Enterprise editions of Visual Studio to the repository's development configuration DSC scripts.
|
||||
- Updated CppWinRT to 2.0.240111.5.
|
||||
- Updated System.Drawing.Common to 8.0.2 to fix CI builds after the .NET 8.0.2 upgrade was released.
|
||||
- Updated WPFUI version to 3.0.0. Thanks [@niels9001](https://github.com/niels9001)!
|
||||
- XAML Styler is now fully tested in the solution when CI runs. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
- Fixed a faulty XAML binding in the Text Extractor settings page.
|
||||
- Updated Microsoft.Web.WebView2 to 1.0.2365.46.
|
||||
- Updated System.Drawing.Common to 8.0.3 to fix CI builds after the .NET 8.0.3 upgrade was released.
|
||||
- Adjusted the GitHub action names for releasing to winget and Microsoft Store so they're clearer in the UI.
|
||||
- Upgraded WinAppSDK to 1.5.1, fixing many related issues.
|
||||
- Consolidate the WebView2 version used by WinUI 2 in the Keyboard Manager Editor.
|
||||
- Unified the use of Precompiled Headers when building on CI. Thanks [@dfederm](https://github.com/dfederm)!
|
||||
- Added UI tests for FancyZones Editor in CI.
|
||||
- Added a GitHub bot to identify possible duplicates when a new issue is created. Thanks [@craigloewen-msft](https://github.com/craigloewen-msft)!
|
||||
- Updated the WiX installer dependency to 3.14.1 to fix possible security issues.
|
||||
- Changed the pipelines to use pipeline artifacts instead of build artifacts. Thanks [@dfederm](https://github.com/dfederm)!
|
||||
- Added the -graph parameter for pipelines. Thanks [@dfederm](https://github.com/dfederm)!
|
||||
- Tests in the pipelines now run as part of the build step to save on CI time. Thanks [@dfederm](https://github.com/dfederm)!
|
||||
|
||||
#### What is being planned for version 0.80
|
||||
#### What is being planned for version 0.81
|
||||
|
||||
For [v0.80][github-next-release-work], we'll work on the items below:
|
||||
For [v0.81][github-next-release-work], we'll work on the items below:
|
||||
|
||||
- Stability / bug fixes
|
||||
- Language selection
|
||||
- Automated UI testing through WinAppDriver
|
||||
- Develop support for Desired State Configuration
|
||||
- New module: File Actions Menu
|
||||
|
||||
The next release is planned to be released during Microsoft Build 2024.
|
||||
|
||||
## PowerToys Community
|
||||
|
||||
The PowerToys team is extremely grateful to have the [support of an amazing active community][community-link]. The work you do is incredibly important. PowerToys wouldn’t be nearly what it is today without your help filing bugs, updating documentation, guiding the design, or writing features. We want to say thank you and take time to recognize your work. Month by month, you directly help make PowerToys a better piece of software.
|
||||
|
||||
@@ -1,85 +1,26 @@
|
||||
## FancyZones Lib
|
||||
# FancyZones UI tests
|
||||
|
||||
#### [`FancyZones.cpp`](/src/modules/fancyzones/lib/FancyZones.cpp)
|
||||
TODO
|
||||
UI tests are implemented using [Windows Application Driver](https://github.com/microsoft/WinAppDriver).
|
||||
|
||||
#### [`Settings.cpp`](/src/modules/fancyzones/lib/Settings.cpp)
|
||||
TODO
|
||||
## Before running tests
|
||||
|
||||
#### [`trace.cpp`](/src/modules/fancyzones/lib/trace.cpp)
|
||||
TODO
|
||||
- Install Windows Application Driver v1.2.1 from https://github.com/microsoft/WinAppDriver/releases/tag/v1.2.1.
|
||||
- Enable Developer Mode in Windows settings
|
||||
|
||||
#### [`Zone.cpp`](/src/modules/fancyzones/lib/Zone.cpp)
|
||||
TODO
|
||||
## Running tests
|
||||
|
||||
- Exit PowerToys if it's running
|
||||
- Run WinAppDriver.exe from the installation directory. Skip this step if installed in the default directory (`C:\Program Files (x86)\Windows Application Driver`); in this case, it'll be launched automatically during tests.
|
||||
- Open `PowerToys.sln` in Visual Studio and build the solution.
|
||||
- Run tests in the Test Explorer (`Test > Test Explorer` or `Ctrl+E, T`).
|
||||
|
||||
#### [`ZoneSet.cpp`](/src/modules/fancyzones/lib/ZoneSet.cpp)
|
||||
TODO
|
||||
>Note: notifications or other application windows, that are shown above the window under test, can disrupt the testing process.
|
||||
|
||||
#### [`WorkArea.cpp`](/src/modules/fancyzones/lib/WorkArea.cpp)
|
||||
TODO
|
||||
|
||||
## FancyZones Editor
|
||||
## Extra tools and information
|
||||
|
||||
#### [`App.xaml.cs`](/src/modules/fancyzones/editor/App.xaml.cs)
|
||||
TODO
|
||||
**Test samples**: https://github.com/microsoft/WinAppDriver/tree/master/Samples
|
||||
|
||||
#### [`Properties\AssemblyInfo.cs`](/src/modules/fancyzones/editor/Properties\AssemblyInfo.cs)
|
||||
TODO
|
||||
|
||||
#### [`CanvasEditor.xaml.cs`](/src/modules/fancyzones/editor/CanvasEditor.xaml.cs)
|
||||
TODO
|
||||
|
||||
#### [`CanvasEditorWindow.xaml.cs`](/src/modules/fancyzones/editor/CanvasEditorWindow.xaml.cs)
|
||||
TODO
|
||||
|
||||
#### [`Models\CanvasLayoutModel.cs`](/src/modules/fancyzones/editor/Models\CanvasLayoutModel.cs)
|
||||
TODO
|
||||
|
||||
#### [`CanvasZone.xaml.cs`](/src/modules/fancyzones/editor/CanvasZone.xaml.cs)
|
||||
TODO
|
||||
|
||||
#### [`EditorOverlay.xaml.cs`](/src/modules/fancyzones/editor/EditorOverlay.xaml.cs)
|
||||
TODO
|
||||
|
||||
#### [`EditorWindow.cs`](/src/modules/fancyzones/editor/EditorWindow.cs)
|
||||
TODO
|
||||
|
||||
#### [`GridEditor.xaml.cs`](/src/modules/fancyzones/editor/GridEditor.xaml.cs)
|
||||
TODO
|
||||
|
||||
#### [`GridEditorWindow.xaml.cs`](/src/modules/fancyzones/editor/GridEditorWindow.xaml.cs)
|
||||
TODO
|
||||
|
||||
#### [`Models\GridLayoutModel.cs`](/src/modules/fancyzones/editor/Models\GridLayoutModel.cs)
|
||||
TODO
|
||||
|
||||
#### [`GridResizer.xaml.cs`](/src/modules/fancyzones/editor/GridResizer.xaml.cs)
|
||||
TODO
|
||||
|
||||
#### [`GridZone.xaml.cs`](/src/modules/fancyzones/editor/GridZone.xaml.cs)
|
||||
TODO
|
||||
|
||||
#### [`Models\LayoutModel.cs`](/src/modules/fancyzones/editor/Models/LayoutModel.cs)
|
||||
TODO
|
||||
|
||||
#### [`LayoutPreview.xaml.cs`](/src/modules/fancyzones/editor/LayoutPreview.xaml.cs)
|
||||
TODO
|
||||
|
||||
#### [`MainWindow.xaml.cs`](/src/modules/fancyzones/editor/MainWindow.xaml.cs)
|
||||
TODO
|
||||
|
||||
#### [`Properties\Resources.Designer.cs`](/src/modules/fancyzones/editor/Properties/Resources.Designer.cs)
|
||||
TODO
|
||||
|
||||
#### [`RowColInfo.cs`](/src/modules/fancyzones/editor/RowColInfo.cs)
|
||||
TODO
|
||||
|
||||
#### [`Models\Settings.cs`](/src/modules/fancyzones/editor/Models/Settings.cs)
|
||||
TODO
|
||||
|
||||
#### [`Properties\Settings.Designer.cs`](/src/modules/fancyzones/editor/Properties/Settings.Designer.cs)
|
||||
TODO
|
||||
|
||||
#### [`WindowLayout.xaml.cs`](/src/modules/fancyzones/editor/WindowLayout.xaml.cs)
|
||||
TODO
|
||||
While working on tests, you may need a tool that helps you to view the element's accessibility data, e.g. for finding the button to click. For this purpose, you could use [AccessibilityInsights](https://accessibilityinsights.io/docs/windows/overview) or [WinAppDriver UI Recorder](https://github.com/microsoft/WinAppDriver/wiki/WinAppDriver-UI-Recorder).
|
||||
|
||||
>Note: close helper tools while running tests. Overlapping windows can affect test results.
|
||||
@@ -80,8 +80,8 @@ The installer can only be compiled in `Release` mode; steps 1 and 2 must be perf
|
||||
### Prerequisites for building the MSI installer
|
||||
|
||||
1. Install the [WiX Toolset Visual Studio 2022 Extension](https://marketplace.visualstudio.com/items?itemName=WixToolset.WixToolsetVisualStudio2022Extension).
|
||||
1. Install the [WiX Toolset build tools](https://github.com/wixtoolset/wix3/releases/tag/wix314rtm). (installer [direct link](https://github.com/wixtoolset/wix3/releases/download/wix314rtm/wix314.exe))
|
||||
1. Download [WiX binaries](https://github.com/wixtoolset/wix3/releases/download/wix314rtm/wix314-binaries.zip) and extract `wix.targets` to `C:\Program Files (x86)\WiX Toolset v3.14`.
|
||||
1. Install the [WiX Toolset build tools](https://github.com/wixtoolset/wix3/releases/tag/wix3141rtm). (installer [direct link](https://github.com/wixtoolset/wix3/releases/download/wix3141rtm/wix314.exe))
|
||||
1. Download [WiX binaries](https://github.com/wixtoolset/wix3/releases/download/wix3141rtm/wix314-binaries.zip) and extract `wix.targets` to `C:\Program Files (x86)\WiX Toolset v3.14`.
|
||||
|
||||
### Building prerequisite projects
|
||||
|
||||
|
||||
98
doc/devdocs/settingsv2/dsc-configure.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# What is it
|
||||
|
||||
We would like to enable our users to use [`winget configure`](https://learn.microsoft.com/en-us/windows/package-manager/winget/configure) command to install PowerToys and configure its settings with a [Winget configuration file](https://learn.microsoft.com/en-us/windows/package-manager/configuration/create). For example:
|
||||
|
||||
```yaml
|
||||
properties:
|
||||
resources:
|
||||
- resource: Microsoft.WinGet.DSC/WinGetPackage
|
||||
directives:
|
||||
description: Install PowerToys
|
||||
allowPrerelease: true
|
||||
settings:
|
||||
id: PowerToys (Preview)
|
||||
source: winget
|
||||
|
||||
- resource: PowerToysConfigure
|
||||
directives:
|
||||
description: Configure PowerToys
|
||||
settings:
|
||||
ShortcutGuide:
|
||||
Enabled: false
|
||||
OverlayOpacity: 1
|
||||
FancyZones:
|
||||
Enabled: true
|
||||
FancyzonesEditorHotkey: "Shift+Ctrl+Alt+F"
|
||||
configurationVersion: 0.2.0
|
||||
```
|
||||
|
||||
This should install PowerToys and make `PowerToysConfigure` resource available. We can use it in the same file.
|
||||
|
||||
# How it works
|
||||
|
||||
`PowerToysConfigure` is a [class-based DSC resource](https://learn.microsoft.com/en-us/powershell/dsc/concepts/class-based-resources?view=dsc-2.0). It looks up whether each setting was specified or not by checking whether it's `$null` or `0` for `enum`s and invokes `PowerToys.Settings.exe` with the updated value like so:
|
||||
```
|
||||
PowerToys.Settings.exe set <ModuleName>.<SettingName> <SettingValue>
|
||||
```
|
||||
|
||||
So for the example the config above should perform 3 following invocations:
|
||||
```
|
||||
PowerToys.Settings.exe set ShortcutGuide.Enabled false
|
||||
PowerToys.Settings.exe set FancyZones.Enabled true
|
||||
PowerToys.Settings.exe set FancyZones.FancyzonesEditorHotkey "Shift+Ctrl+Alt+F"
|
||||
```
|
||||
|
||||
`PowerToys.Settings` uses dotnet reflection capabilities to determine `SettingName` type and tries to convert the supplied `SettingValue` string accordingly. We use `ICmdReprParsable` for custom setting types.
|
||||
|
||||
|
||||
# How DSC is implemented
|
||||
|
||||
We use `PowerToys.Settings.DSC.Schema.Generator` to generate the bulk of `PowerToysConfigure.psm1` and `PowerToysConfigure.psd1` files. It also uses dotnet reflection capabilities to inspect `PowerToys.Settings.UI.Lib.dll` assembly and generate properties for the modules we have. The actual generation is done as a `PowerToys.Settings.DSC.Schema.Generator.csproj` post-build action.
|
||||
|
||||
# Debugging DSC resources
|
||||
|
||||
First, make sure that PowerShell 7.4+ is installed. Then make sure that you have DSC installed:
|
||||
|
||||
```ps
|
||||
Install-Module -Name PSDesiredStateConfiguration -RequiredVersion 2.0.7
|
||||
```
|
||||
|
||||
After that, start a new `pwsh` session and `cd` to `src\dsc\Microsoft.PowerToys.Configure\Generated` directory. From there, you should execute:
|
||||
```ps
|
||||
$env:PSModulePath += ";$pwd"
|
||||
```
|
||||
|
||||
You should have the generated `Microsoft.PowerToys.Configure.psm1` and `Microsoft.PowerToys.Configure.psd1` files inside the `src\dsc\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\0.0.1\` folder.
|
||||
|
||||
This will allow DSC to discover our DSC Resource module. See [PSModulePath](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_psmodulepath?view=powershell-7.4#long-description) for more info.
|
||||
|
||||
If everything works, you should see that your module is discovered by executing the following command:
|
||||
|
||||
```ps
|
||||
Get-Module -ListAvailable | grep PowerToys
|
||||
```
|
||||
|
||||
The resource itself should also be available:
|
||||
```ps
|
||||
Get-DSCResource | grep PowerToys
|
||||
```
|
||||
|
||||
Otherwise, you can force-import the module to diagnose issues:
|
||||
|
||||
```
|
||||
Import-Module .\Microsoft.PowerToys.Configure.psd1
|
||||
```
|
||||
|
||||
If it's imported successfully, you could also try to invoke it directly:
|
||||
|
||||
```ps
|
||||
Invoke-DscResource -Name PowerToysConfigure -Method Set -ModuleName Microsoft.PowerToys.Configure -Property @{ Debug = $true; Awake = @{ Enabled = $false; Mode = "TIMED"; IntervalMinutes = "10" } }
|
||||
```
|
||||
|
||||
Note that we've supplied `Debug` option, so a `%TEMP\PowerToys.DSC.TestConfigure.txt` is created with the supplied properties, a current timestamp, and other debug output.
|
||||
|
||||
Finally, you can test it with winget by invoking it as such:
|
||||
|
||||
```ps
|
||||
winget configure .\configuration.dsc.yaml --accept-configuration-agreements --disable-interactivity
|
||||
```
|
||||
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 206 KiB After Width: | Height: | Size: 131 KiB |
@@ -46,6 +46,51 @@
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
|
||||
<?if $(var.PerUser) = "true" ?>
|
||||
<DirectoryRef Id="PersonalFolder">
|
||||
<Directory Id="WindowsPowerShellFolder" Name="PowerShell">
|
||||
<Directory Id="PowerShellModulesFolder" Name="Modules">
|
||||
<Directory Id="PowerToysDscFolder" Name="Microsoft.PowerToys.Configure">
|
||||
<Directory Id="PowerToysDscVerFolder" Name="$(var.Version)">
|
||||
<Component Id="PowerToysDSC" Win64="yes" Guid="4A033E3B-6590-43FD-8FBD-27F9DF557F7F">
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\[Manufacturer]\[ProductName]"
|
||||
Name="DSCInstalled"
|
||||
Type="integer"
|
||||
Value="1"
|
||||
KeyPath="yes"/>
|
||||
<!-- Don't fail installation because of DSC. Files are marked as not vital. -->
|
||||
<File Vital="no" Source="$(var.RepoDir)\src\dsc\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\$(var.Version)\Microsoft.PowerToys.Configure.psd1" Id="PTConf.psd1" />
|
||||
<File Vital="no" Source="$(var.RepoDir)\src\dsc\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\$(var.Version)\Microsoft.PowerToys.Configure.psm1" Id="PTConf.psm1" />
|
||||
<RemoveFolder Id="RemoveThisFolder" On="uninstall" />
|
||||
<RemoveFolder Id="RemovePowerToysDscVerFolder" Directory="PowerToysDscVerFolder" On="uninstall" />
|
||||
<RemoveFolder Id="RemovePowerToysDscFolder" Directory="PowerToysDscFolder" On="uninstall" />
|
||||
<RemoveFolder Id="RemovePowerShellModulesFolder" Directory="PowerShellModulesFolder" On="uninstall" />
|
||||
<RemoveFolder Id="RemoveWindowsPowerShellFolder" Directory="WindowsPowerShellFolder" On="uninstall" />
|
||||
</Component>
|
||||
</Directory>
|
||||
</Directory>
|
||||
</Directory>
|
||||
</Directory>
|
||||
</DirectoryRef>
|
||||
<?else?>
|
||||
<DirectoryRef Id="ProgramFiles64Folder">
|
||||
<Directory Id="WindowsPowerShellFolder" Name="WindowsPowerShell">
|
||||
<Directory Id="PowerShellModulesFolder" Name="Modules">
|
||||
<Directory Id="PowerToysDscFolder" Name="Microsoft.PowerToys.Configure">
|
||||
<Directory Id="PowerToysDscVerFolder" Name="$(var.Version)">
|
||||
<Component Id="PowerToysDSC" Win64="yes" Guid="C52AECA0-DA73-49B8-BB49-31EF6640FF1F">
|
||||
<!-- Don't fail installation because of DSC. Files are marked as not vital. -->
|
||||
<File Vital="no" Source="$(var.RepoDir)\src\dsc\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\$(var.Version)\Microsoft.PowerToys.Configure.psd1" Id="PTConf.psd1" />
|
||||
<File Vital="no" Source="$(var.RepoDir)\src\dsc\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\$(var.Version)\Microsoft.PowerToys.Configure.psm1" Id="PTConf.psm1" />
|
||||
</Component>
|
||||
</Directory>
|
||||
</Directory>
|
||||
</Directory>
|
||||
</Directory>
|
||||
</DirectoryRef>
|
||||
<?endif?>
|
||||
|
||||
<DirectoryRef Id="ApplicationProgramsFolder">
|
||||
<Component Id="PowerToysStartMenuShortcut" >
|
||||
<Shortcut Id="ApplicationStartMenuShortcut"
|
||||
@@ -101,6 +146,7 @@
|
||||
<ComponentRef Id="License_rtf" />
|
||||
<ComponentRef Id="Notice_md" />
|
||||
<ComponentRef Id="DesktopShortcut" />
|
||||
<ComponentRef Id="PowerToysDSC" />
|
||||
</ComponentGroup>
|
||||
</Fragment>
|
||||
</Wix>
|
||||
|
||||
@@ -25,6 +25,11 @@
|
||||
<?if $(sys.BUILDARCH) = x64 ?>
|
||||
<File Source="$(var.BinDir)KeyboardManagerEditor\vcruntime140_1_app.dll" />
|
||||
<?endif ?>
|
||||
<!-- Latest CppWinRT upgrade made Keyboard Manager Editor depend on additional VC Runtime libraries. -->
|
||||
<!-- These are not in the Keyboard Manager Editor build output. So we copy them from the base build directory. -->
|
||||
<File Source="$(var.BinDir)vcruntime140.dll" />
|
||||
<File Source="$(var.BinDir)vcruntime140_1.dll" />
|
||||
<File Source="$(var.BinDir)msvcp140.dll" />
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
Vital="no">
|
||||
</ExePackage>
|
||||
<ExePackage
|
||||
DisplayName="Installing Microsoft Edge WebView2"
|
||||
DisplayName="Microsoft Edge WebView2"
|
||||
Name="MicrosoftEdgeWebview2Setup.exe"
|
||||
Compressed="yes"
|
||||
Id="WebView2"
|
||||
@@ -81,7 +81,7 @@
|
||||
UninstallCommand="/silent /uninstall">
|
||||
</ExePackage>
|
||||
<MsiPackage
|
||||
DisplayName="Installing PowerToys"
|
||||
DisplayName="PowerToys MSI"
|
||||
SourceFile="$(var.PowerToysPlatform)\Release\$(var.MSIPath)\$(var.MSIName)"
|
||||
Compressed="yes"
|
||||
DisplayInternalUI="no">
|
||||
|
||||
@@ -395,6 +395,7 @@
|
||||
<Directory Id="ApplicationProgramsFolder" Name="PowerToys (Preview)"/>
|
||||
</Directory>
|
||||
<Directory Id="DesktopFolder" Name="Desktop" />
|
||||
<Directory Id="PersonalFolder" Name="UserHomeDocuments" />
|
||||
</Directory>
|
||||
</Fragment>
|
||||
</Wix>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.MSBuildCache.AzurePipelines" version="0.1.258-preview" />
|
||||
<package id="Microsoft.MSBuildCache.Local" version="0.1.258-preview" />
|
||||
<package id="Microsoft.MSBuildCache.SharedCompilation" version="0.1.258-preview" />
|
||||
<package id="Microsoft.MSBuildCache.AzurePipelines" version="0.1.271-preview" />
|
||||
<package id="Microsoft.MSBuildCache.Local" version="0.1.271-preview" />
|
||||
<package id="Microsoft.MSBuildCache.SharedCompilation" version="0.1.271-preview" />
|
||||
</packages>
|
||||
@@ -65,7 +65,7 @@ namespace Common.UI
|
||||
case SettingsWindow.MeasureTool:
|
||||
return "MeasureTool";
|
||||
case SettingsWindow.PowerOCR:
|
||||
return "PowerOCR";
|
||||
return "PowerOcr";
|
||||
case SettingsWindow.RegistryPreview:
|
||||
return "RegistryPreview";
|
||||
case SettingsWindow.CropAndLock:
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
#include <common/version/version.h>
|
||||
|
||||
|
||||
using namespace System;
|
||||
using namespace System::Runtime::InteropServices;
|
||||
using System::Collections::Generic::List;
|
||||
@@ -40,11 +39,17 @@ public
|
||||
delete _map;
|
||||
}
|
||||
|
||||
String ^ GetKeyName(DWORD key) {
|
||||
String ^ GetKeyName(DWORD key)
|
||||
{
|
||||
return gcnew String(_map->GetKeyName(key).c_str());
|
||||
}
|
||||
|
||||
void Updatelayout()
|
||||
DWORD GetKeyValue(String ^ name)
|
||||
{
|
||||
return _map->GetKeyFromName(msclr::interop::marshal_as<std::wstring>(name));
|
||||
}
|
||||
|
||||
void Updatelayout()
|
||||
{
|
||||
_map->UpdateLayout();
|
||||
}
|
||||
@@ -129,13 +134,13 @@ public
|
||||
}
|
||||
|
||||
static List<String ^> ^ GetAllActiveMicrophoneDeviceNames() {
|
||||
auto names = gcnew List<String ^>();
|
||||
for (const auto& device : MicrophoneDevice::getAllActive())
|
||||
{
|
||||
names->Add(gcnew String(device->name().data()));
|
||||
auto names = gcnew List<String ^>();
|
||||
for (const auto& device : MicrophoneDevice::getAllActive())
|
||||
{
|
||||
names->Add(gcnew String(device->name().data()));
|
||||
}
|
||||
return names;
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
static List<String ^> ^
|
||||
GetAllVideoCaptureDeviceNames() {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
properties:
|
||||
resources:
|
||||
# - resource: Microsoft.WinGet.DSC/WinGetPackage
|
||||
# directives:
|
||||
# description: Install PowerToys
|
||||
# allowPrerelease: true
|
||||
# settings:
|
||||
# id: PowerToys (Preview)
|
||||
# source: winget
|
||||
|
||||
- resource: PowerToysConfigure
|
||||
directives:
|
||||
description: Configure PowerToys
|
||||
settings:
|
||||
ShortcutGuide:
|
||||
Enabled: false
|
||||
OverlayOpacity: 50
|
||||
FancyZones:
|
||||
Enabled: true
|
||||
FancyzonesEditorHotkey: "Shift+Ctrl+Alt+F"
|
||||
FileLocksmith:
|
||||
Enabled: false
|
||||
configurationVersion: 0.2.0
|
||||
@@ -0,0 +1,53 @@
|
||||
properties:
|
||||
resources:
|
||||
- resource: PowerToysConfigure
|
||||
directives:
|
||||
description: Configure PowerToys
|
||||
settings:
|
||||
PowerLauncher:
|
||||
Enabled: true
|
||||
Plugins:
|
||||
- Name: "Calculator"
|
||||
Disabled: false
|
||||
- Name: "Folder"
|
||||
Disabled: false
|
||||
- Name: "History"
|
||||
Disabled: false
|
||||
- Name: "Windows Search"
|
||||
Disabled: false
|
||||
- Name: "OneNote"
|
||||
Disabled: false
|
||||
- Name: "PowerToys"
|
||||
Disabled: false
|
||||
- Name: "Program"
|
||||
Disabled: false
|
||||
ActionKeyword: "P:"
|
||||
IsGlobal: false
|
||||
- Name: "Registry Plugin"
|
||||
Disabled: false
|
||||
- Name: "Service"
|
||||
Disabled: false
|
||||
- Name: "Shell"
|
||||
Disabled: false
|
||||
- Name: "Windows System Commands"
|
||||
Disabled: false
|
||||
- Name: "Time and Date"
|
||||
Disabled: false
|
||||
- Name: "Unit Converter"
|
||||
Disabled: false
|
||||
- Name: "URI Handler"
|
||||
Disabled: false
|
||||
- Name: "Value Generator"
|
||||
Disabled: false
|
||||
- Name: "Visual Studio Code Workspaces"
|
||||
Disabled: false
|
||||
- Name: "Web Search"
|
||||
Disabled: false
|
||||
- Name: "Windows settings"
|
||||
Disabled: false
|
||||
- Name: "Windows Terminal"
|
||||
Disabled: false
|
||||
- Name: "Window Walker"
|
||||
Disabled: false
|
||||
|
||||
configurationVersion: 0.2.0
|
||||
@@ -0,0 +1,70 @@
|
||||
properties:
|
||||
resources:
|
||||
- resource: PowerToysConfigure
|
||||
directives:
|
||||
description: Configure PowerToys
|
||||
settings:
|
||||
AlwaysOnTop:
|
||||
Enabled: false
|
||||
Awake:
|
||||
Enabled: false
|
||||
ColorPicker:
|
||||
Enabled: false
|
||||
CropAndLock:
|
||||
Enabled: false
|
||||
EnvironmentVariables:
|
||||
Enabled: false
|
||||
FancyZones:
|
||||
Enabled: false
|
||||
FileLocksmith:
|
||||
Enabled: false
|
||||
ImageResizer:
|
||||
Enabled: false
|
||||
KeyboardManager:
|
||||
Enabled: false
|
||||
FindMyMouse:
|
||||
Enabled: false
|
||||
MouseHighlighter:
|
||||
Enabled: false
|
||||
MouseJump:
|
||||
Enabled: false
|
||||
MousePointerCrosshairs:
|
||||
Enabled: false
|
||||
MouseWithoutBorders:
|
||||
Enabled: false
|
||||
Peek:
|
||||
Enabled: false
|
||||
PowerRename:
|
||||
Enabled: false
|
||||
PowerLauncher:
|
||||
Enabled: false
|
||||
PowerAccent:
|
||||
Enabled: false
|
||||
PowerPreview:
|
||||
EnableSvgPreview: false
|
||||
EnableSvgThumbnail: false
|
||||
EnableMdPreview: false
|
||||
EnableMonacoPreview: false
|
||||
EnablePdfPreview: false
|
||||
EnablePdfThumbnail: false
|
||||
EnableGcodePreview: false
|
||||
EnableGcodeThumbnail: false
|
||||
EnableStlThumbnail: false
|
||||
EnableQoiPreview: false
|
||||
EnableQoiThumbnail: false
|
||||
PowerOcr:
|
||||
Enabled: false
|
||||
ShortcutGuide:
|
||||
Enabled: false
|
||||
VideoConference:
|
||||
Enabled: false
|
||||
MeasureTool:
|
||||
Enabled: false
|
||||
Hosts:
|
||||
Enabled: false
|
||||
PastePlain:
|
||||
Enabled: false
|
||||
RegistryPreview:
|
||||
Enabled: false
|
||||
|
||||
configurationVersion: 0.2.0
|
||||
@@ -0,0 +1,70 @@
|
||||
properties:
|
||||
resources:
|
||||
- resource: PowerToysConfigure
|
||||
directives:
|
||||
description: Configure PowerToys
|
||||
settings:
|
||||
AlwaysOnTop:
|
||||
Enabled: true
|
||||
Awake:
|
||||
Enabled: true
|
||||
ColorPicker:
|
||||
Enabled: true
|
||||
CropAndLock:
|
||||
Enabled: true
|
||||
EnvironmentVariables:
|
||||
Enabled: true
|
||||
FancyZones:
|
||||
Enabled: true
|
||||
FileLocksmith:
|
||||
Enabled: true
|
||||
ImageResizer:
|
||||
Enabled: true
|
||||
KeyboardManager:
|
||||
Enabled: true
|
||||
FindMyMouse:
|
||||
Enabled: true
|
||||
MouseHighlighter:
|
||||
Enabled: true
|
||||
MouseJump:
|
||||
Enabled: true
|
||||
MousePointerCrosshairs:
|
||||
Enabled: true
|
||||
MouseWithoutBorders:
|
||||
Enabled: true
|
||||
Peek:
|
||||
Enabled: true
|
||||
PowerRename:
|
||||
Enabled: true
|
||||
PowerLauncher:
|
||||
Enabled: true
|
||||
PowerAccent:
|
||||
Enabled: true
|
||||
PowerPreview:
|
||||
EnableSvgPreview: true
|
||||
EnableSvgThumbnail: true
|
||||
EnableMdPreview: true
|
||||
EnableMonacoPreview: true
|
||||
EnablePdfPreview: true
|
||||
EnablePdfThumbnail: true
|
||||
EnableGcodePreview: true
|
||||
EnableGcodeThumbnail: true
|
||||
EnableStlThumbnail: true
|
||||
EnableQoiPreview: true
|
||||
EnableQoiThumbnail: true
|
||||
PowerOcr:
|
||||
Enabled: true
|
||||
ShortcutGuide:
|
||||
Enabled: true
|
||||
VideoConference:
|
||||
Enabled: true
|
||||
MeasureTool:
|
||||
Enabled: true
|
||||
Hosts:
|
||||
Enabled: true
|
||||
PastePlain:
|
||||
Enabled: true
|
||||
RegistryPreview:
|
||||
Enabled: true
|
||||
|
||||
configurationVersion: 0.2.0
|
||||
31
src/dsc/PowerToys.Settings.DSC.Schema.Generator/Common.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PowerToys.Settings.DSC.Schema;
|
||||
|
||||
internal sealed class Common
|
||||
{
|
||||
private static string[] TypeParts(string name)
|
||||
{
|
||||
return Regex.Split(name.ToLower(CultureInfo.CurrentCulture), @"(?<!^)(?=[A-Z])|\.");
|
||||
}
|
||||
|
||||
internal static bool InferIsBool(Type propertyInfo)
|
||||
{
|
||||
return TypeParts(propertyInfo.Name).Any(word => word.Equals("Bool", StringComparison.OrdinalIgnoreCase) || word.Equals("Boolean", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
internal static bool InferIsInt(Type propertyInfo)
|
||||
{
|
||||
return TypeParts(propertyInfo.Name).Any(word => word.Contains("Int", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
477
src/dsc/PowerToys.Settings.DSC.Schema.Generator/DSCGeneration.cs
Normal file
@@ -0,0 +1,477 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using static PowerToys.Settings.DSC.Schema.Introspection;
|
||||
|
||||
namespace PowerToys.Settings.DSC.Schema;
|
||||
|
||||
internal sealed class DSCGeneration
|
||||
{
|
||||
private static readonly string DoubleNewLine = Environment.NewLine + Environment.NewLine;
|
||||
|
||||
private struct AdditionalPropertiesInfo
|
||||
{
|
||||
public string Name;
|
||||
|
||||
public string Type;
|
||||
}
|
||||
|
||||
private static readonly Dictionary<string, AdditionalPropertiesInfo> AdditionalPropertiesInfoPerModule = new Dictionary<string, AdditionalPropertiesInfo> { { "PowerLauncher", new AdditionalPropertiesInfo { Name = "Plugins", Type = "Hashtable[]" } } };
|
||||
|
||||
private static string EmitEnumDefinition(Type type)
|
||||
{
|
||||
var values = string.Empty;
|
||||
|
||||
int i = 0;
|
||||
foreach (var name in Enum.GetNames(type))
|
||||
{
|
||||
values += " " + name;
|
||||
|
||||
// Nullable enums seem to be not supported by winget, so the workaround is to always start with '1', because by default the values are initialized to zero. That allows us to use zero as a "lack of value" indicator.
|
||||
if (i == 0)
|
||||
{
|
||||
values += " = 1";
|
||||
}
|
||||
|
||||
values += Environment.NewLine;
|
||||
i++;
|
||||
}
|
||||
|
||||
return $$"""
|
||||
enum {{type.Name}} {
|
||||
{{values}}}
|
||||
""";
|
||||
}
|
||||
|
||||
private struct PropertyEmitInfo
|
||||
{
|
||||
public string Name;
|
||||
public string Type;
|
||||
public string Initializer;
|
||||
public string EqualityOperator;
|
||||
public string DefaultValue;
|
||||
|
||||
public PropertyEmitInfo(string name, Type property)
|
||||
{
|
||||
Name = name;
|
||||
|
||||
bool intLike = Common.InferIsInt(property);
|
||||
bool boolLike = Common.InferIsBool(property);
|
||||
|
||||
var rawType = "string";
|
||||
var isNullable = true;
|
||||
DefaultValue = "$null";
|
||||
EqualityOperator = "-ne";
|
||||
Initializer = "= $null";
|
||||
|
||||
if (intLike)
|
||||
{
|
||||
rawType = "int";
|
||||
isNullable = false;
|
||||
}
|
||||
else if (boolLike)
|
||||
{
|
||||
rawType = "bool";
|
||||
isNullable = false;
|
||||
}
|
||||
else if (property.IsEnum)
|
||||
{
|
||||
rawType = property.Name;
|
||||
isNullable = true;
|
||||
Initializer = string.Empty;
|
||||
DefaultValue = "0";
|
||||
}
|
||||
|
||||
// For strings
|
||||
else
|
||||
{
|
||||
EqualityOperator = "-notlike";
|
||||
DefaultValue = "''";
|
||||
}
|
||||
|
||||
// We must make all our properties nullable to be able to detect which of them weren't supplied
|
||||
Type = isNullable ? rawType : $"Nullable[{rawType}]";
|
||||
}
|
||||
}
|
||||
|
||||
private static string EmitPropertyDefinition(PropertyEmitInfo info)
|
||||
{
|
||||
return $$"""
|
||||
[DscProperty()] [{{info.Type}}]
|
||||
${{info.Name}} {{info.Initializer}}
|
||||
""";
|
||||
}
|
||||
|
||||
private static string EmitPropertyApplyChangeStatements(string moduleName, PropertyEmitInfo info, string localPropertyName = null)
|
||||
{
|
||||
if (localPropertyName == null)
|
||||
{
|
||||
localPropertyName = info.Name;
|
||||
}
|
||||
|
||||
return $$"""
|
||||
if ($this.{{localPropertyName}} {{info.EqualityOperator}} {{info.DefaultValue}}) {
|
||||
$Changes.Value += "set {{moduleName}}.{{info.Name}} `"$($this.{{localPropertyName}})`""
|
||||
}
|
||||
""";
|
||||
}
|
||||
|
||||
private static string EmitModuleDefinition(SettingsStructure module)
|
||||
{
|
||||
bool generalSettings = module.Name == "GeneralSettings";
|
||||
|
||||
var properties = module.Properties
|
||||
.Where(property => !property.Value.IsIgnored)
|
||||
.Select(property => new PropertyEmitInfo(property.Key, property.Value.Type));
|
||||
|
||||
var propertyDefinitionsBlock = string.Empty;
|
||||
var applyChangesBlock = string.Empty;
|
||||
|
||||
foreach (var property in properties)
|
||||
{
|
||||
var definition = EmitPropertyDefinition(property);
|
||||
var applyChanges = EmitPropertyApplyChangeStatements(module.Name, property);
|
||||
|
||||
propertyDefinitionsBlock += definition + DoubleNewLine;
|
||||
applyChangesBlock += applyChanges + DoubleNewLine;
|
||||
}
|
||||
|
||||
bool hasAdditionalProperties = AdditionalPropertiesInfoPerModule.TryGetValue(module.Name, out var additionalPropertiesInfo);
|
||||
|
||||
// Enabled property of each module is contained in General settings
|
||||
if (!generalSettings)
|
||||
{
|
||||
propertyDefinitionsBlock += $$"""
|
||||
[DscProperty(Key)] [Nullable[bool]]
|
||||
$Enabled = $null
|
||||
|
||||
""";
|
||||
|
||||
if (hasAdditionalProperties)
|
||||
{
|
||||
propertyDefinitionsBlock += $$"""
|
||||
|
||||
[DscProperty()] [{{additionalPropertiesInfo.Type}}]
|
||||
${{additionalPropertiesInfo.Name}} = @()
|
||||
|
||||
|
||||
""";
|
||||
}
|
||||
|
||||
applyChangesBlock += EmitPropertyApplyChangeStatements("General.Enabled", new PropertyEmitInfo($"{module.Name}", typeof(bool)), "Enabled");
|
||||
}
|
||||
|
||||
var additionalPropertiesCheckBlock = string.Empty;
|
||||
if (hasAdditionalProperties)
|
||||
{
|
||||
additionalPropertiesCheckBlock = $$"""
|
||||
if ($this.{{additionalPropertiesInfo.Name}}.Count -gt 0) {
|
||||
$AdditionalPropertiesTmpPath = [System.IO.Path]::GetTempFileName()
|
||||
$this.{{additionalPropertiesInfo.Name}} | ConvertTo-Json | Set-Content -Path $AdditionalPropertiesTmpPath
|
||||
$Changes.Value += "setAdditional {{module.Name}} `"$AdditionalPropertiesTmpPath`""
|
||||
}
|
||||
""";
|
||||
}
|
||||
|
||||
return $$"""
|
||||
class {{module.Name}} {
|
||||
{{propertyDefinitionsBlock}} ApplyChanges([ref]$Changes) {
|
||||
{{applyChangesBlock}}
|
||||
|
||||
{{additionalPropertiesCheckBlock}}
|
||||
}
|
||||
}
|
||||
|
||||
""";
|
||||
}
|
||||
|
||||
public static string EmitModuleFileContents(SettingsStructure[] moduleSettings, SettingsStructure generalSettings, string debugSettingsPath)
|
||||
{
|
||||
var enumsToEmit = new HashSet<Type>();
|
||||
|
||||
var modulesBlock = string.Empty;
|
||||
var modulesResourcePropertiesBlock = string.Empty;
|
||||
var applyModulesChangesBlock = string.Empty;
|
||||
|
||||
foreach (var module in moduleSettings.Append(generalSettings))
|
||||
{
|
||||
enumsToEmit.UnionWith(module.Properties
|
||||
.Where(property => property.Value.Type.IsEnum)
|
||||
.Select(property => property.Value.Type));
|
||||
|
||||
modulesBlock += EmitModuleDefinition(module);
|
||||
|
||||
applyModulesChangesBlock += $$"""
|
||||
$this.{{module.Name}}.ApplyChanges([ref]$ChangesToApply)
|
||||
|
||||
""";
|
||||
|
||||
modulesResourcePropertiesBlock += $$"""
|
||||
[DscProperty()]
|
||||
[{{module.Name}}]${{module.Name}} = [{{module.Name}}]::new()
|
||||
|
||||
|
||||
""";
|
||||
}
|
||||
|
||||
var enumsBlock = string.Join(DoubleNewLine, enumsToEmit.Select(EmitEnumDefinition));
|
||||
var version = interop.CommonManaged.GetProductVersion().Replace("v", string.Empty);
|
||||
var outputResult = string.Empty;
|
||||
|
||||
outputResult += $$"""
|
||||
#region enums
|
||||
enum PowerToysConfigureEnsure {
|
||||
Absent
|
||||
Present
|
||||
}
|
||||
|
||||
{{enumsBlock}}
|
||||
#endregion enums
|
||||
|
||||
#region DscResources
|
||||
{{modulesBlock}}
|
||||
[DscResource()]
|
||||
class PowerToysConfigure {
|
||||
[DscProperty(Key)] [PowerToysConfigureEnsure]
|
||||
$Ensure = [PowerToysConfigureEnsure]::Present
|
||||
|
||||
[bool] $Debug = $false
|
||||
|
||||
{{modulesResourcePropertiesBlock}}
|
||||
|
||||
""";
|
||||
|
||||
#if DEBUG
|
||||
// Only output PowerToysSettings local build for debug builds. No need to expose release build locations.
|
||||
outputResult += $$"""
|
||||
[string] GetPowerToysSettingsPath() {
|
||||
if ($this.Debug -eq $true) {
|
||||
$SettingsExePath = "{{debugSettingsPath}}"
|
||||
} else {
|
||||
$installation = Get-CimInstance Win32_Product | Where-Object {$_.Name -eq "PowerToys (Preview)" -and $_.Version -eq "{{version}}"}
|
||||
|
||||
if ($installation) {
|
||||
$SettingsExePath = Join-Path (Join-Path $installation.InstallLocation WinUI3Apps) PowerToys.Settings.exe
|
||||
$SettingsExePath = "`"$SettingsExePath`""
|
||||
} else {
|
||||
throw "PowerToys installation wasn't found."
|
||||
}
|
||||
}
|
||||
|
||||
return $SettingsExePath
|
||||
}
|
||||
|
||||
""";
|
||||
#else
|
||||
outputResult += $$"""
|
||||
[string] GetPowerToysSettingsPath() {
|
||||
$installation = Get-CimInstance Win32_Product | Where-Object {$_.Name -eq "PowerToys (Preview)" -and $_.Version -eq "{{version}}"}
|
||||
|
||||
if ($installation) {
|
||||
$SettingsExePath = Join-Path (Join-Path $installation.InstallLocation WinUI3Apps) PowerToys.Settings.exe
|
||||
$SettingsExePath = "`"$SettingsExePath`""
|
||||
} else {
|
||||
throw "PowerToys installation wasn't found."
|
||||
}
|
||||
|
||||
return $SettingsExePath
|
||||
}
|
||||
|
||||
""";
|
||||
#endif
|
||||
|
||||
outputResult += $$"""
|
||||
|
||||
[PowerToysConfigure] Get() {
|
||||
$CurrentState = [PowerToysConfigure]::new()
|
||||
$SettingsExePath = $this.GetPowerToysSettingsPath()
|
||||
$SettingsTmpFilePath = [System.IO.Path]::GetTempFileName()
|
||||
|
||||
$SettingsToRequest = @{}
|
||||
foreach ($module in $CurrentState.PSObject.Properties) {
|
||||
$moduleName = $module.Name
|
||||
# Skip utility properties
|
||||
if ($moduleName -eq "Ensure" -or $moduleName -eq "Debug") {
|
||||
continue
|
||||
}
|
||||
|
||||
$moduleProperties = $module.Value
|
||||
$propertiesArray = @()
|
||||
foreach ($property in $moduleProperties.PSObject.Properties) {
|
||||
$propertyName = $property.Name
|
||||
# Skip Enabled properties - they should be requested from GeneralSettings
|
||||
if ($propertyName -eq "Enabled") {
|
||||
continue
|
||||
}
|
||||
|
||||
$propertiesArray += $propertyName
|
||||
}
|
||||
|
||||
$SettingsToRequest[$moduleName] = $propertiesArray
|
||||
}
|
||||
|
||||
$settingsJson = $SettingsToRequest | ConvertTo-Json
|
||||
$settingsJson | Set-Content -Path $SettingsTmpFilePath
|
||||
|
||||
Start-Process -FilePath $SettingsExePath -Wait -Args "get `"$SettingsTmpFilePath`""
|
||||
$SettingsValues = Get-Content -Path $SettingsTmpFilePath -Raw
|
||||
|
||||
if ($this.Debug -eq $true) {
|
||||
$TempFilePath = Join-Path -Path $env:TEMP -ChildPath "PowerToys.DSC.TestConfigure.txt"
|
||||
Set-Content -Path "$TempFilePath" -Value ("Requested:`r`n" + $settingsJson + "`r`n" + "Got:`r`n" + $SettingsValues + "`r`n" + (Get-Date -Format "o")) -Force
|
||||
}
|
||||
|
||||
$SettingsValues = $SettingsValues | ConvertFrom-Json
|
||||
foreach ($module in $SettingsValues.PSObject.Properties) {
|
||||
$moduleName = $module.Name
|
||||
$obtainedModuleSettings = $module.Value
|
||||
$moduleRef = $CurrentState.$moduleName
|
||||
foreach ($property in $obtainedModuleSettings.PSObject.Properties) {
|
||||
$propertyName = $property.Name
|
||||
$moduleRef.$propertyName = $property.Value
|
||||
}
|
||||
}
|
||||
|
||||
Remove-Item -Path $SettingsTmpFilePath
|
||||
|
||||
return $CurrentState
|
||||
}
|
||||
|
||||
[bool] Test() {
|
||||
# NB: we must always assume that the configuration isn't applied, because changing some settings produce external side-effects
|
||||
return $false
|
||||
}
|
||||
|
||||
[void] Set() {
|
||||
$SettingsExePath = $this.GetPowerToysSettingsPath()
|
||||
$ChangesToApply = @()
|
||||
|
||||
{{applyModulesChangesBlock}}
|
||||
if ($this.Debug -eq $true) {
|
||||
$tmp_info = $ChangesToApply
|
||||
# $tmp_info = $this | ConvertTo-Json -Depth 10
|
||||
|
||||
$TempFilePath = Join-Path -Path $env:TEMP -ChildPath "PowerToys.DSC.TestConfigure.txt"
|
||||
Set-Content -Path "$TempFilePath" -Value ($tmp_info + "`r`n" + (Get-Date -Format "o")) -Force
|
||||
}
|
||||
|
||||
# Stop any running PowerToys instances
|
||||
Stop-Process -Name "PowerToys.Settings" -Force -PassThru | Wait-Process
|
||||
$PowerToysProcessStopped = Stop-Process -Name "PowerToys" -Force -PassThru
|
||||
$PowerToysProcessStopped | Wait-Process
|
||||
|
||||
foreach ($change in $ChangesToApply) {
|
||||
Start-Process -FilePath $SettingsExePath -Wait -Args "$change"
|
||||
}
|
||||
|
||||
# If the PowerToys process was stopped, restart it.
|
||||
if ($PowerToysProcessStopped -ne $null) {
|
||||
Start-Process -FilePath $SettingsExePath
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion DscResources
|
||||
""";
|
||||
|
||||
return outputResult;
|
||||
}
|
||||
|
||||
public static string EmitManifestFileContents()
|
||||
{
|
||||
var version = interop.CommonManaged.GetProductVersion().Replace("v", string.Empty);
|
||||
var generatedDate = DateTime.Now.ToString("dd.MM.yyyy", CultureInfo.InvariantCulture);
|
||||
|
||||
return $$"""
|
||||
#
|
||||
# Module manifest for module 'Microsoft.PowerToys.Configure'
|
||||
#
|
||||
# Generated by: Microsoft Corporation
|
||||
#
|
||||
# Generated on: {{generatedDate}}
|
||||
#
|
||||
|
||||
@{
|
||||
|
||||
# Script module or binary module file associated with this manifest.
|
||||
RootModule = 'Microsoft.PowerToys.Configure.psm1'
|
||||
|
||||
# Version number of this module.
|
||||
ModuleVersion = '{{version}}'
|
||||
|
||||
# ID used to uniquely identify this module
|
||||
GUID = '778ed7a1-489d-4dc9-b0f2-2da3b1fe14cb'
|
||||
|
||||
# Author of this module
|
||||
Author = 'Microsoft Corporation'
|
||||
|
||||
# Company or vendor of this module
|
||||
CompanyName = 'Microsoft'
|
||||
|
||||
# Copyright statement for this module
|
||||
Copyright = '(c) Microsoft Corporation. All rights reserved.'
|
||||
|
||||
# Description of the functionality provided by this module
|
||||
Description = 'The module enables settings configuration for an installed PowerToys application.'
|
||||
|
||||
# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
|
||||
FunctionsToExport = '*'
|
||||
|
||||
# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
|
||||
CmdletsToExport = @()
|
||||
|
||||
# Variables to export from this module
|
||||
VariablesToExport = @()
|
||||
|
||||
# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
|
||||
AliasesToExport = @()
|
||||
|
||||
# DSC resources to export from this module
|
||||
DscResourcesToExport = @(
|
||||
'PowerToysConfigure'
|
||||
)
|
||||
|
||||
# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
|
||||
PrivateData = @{
|
||||
|
||||
PSData = @{
|
||||
|
||||
# Tags applied to this module. These help with module discovery in online galleries.
|
||||
# Tags = @()
|
||||
|
||||
# A URL to the license for this module.
|
||||
# LicenseUri = ''
|
||||
|
||||
# A URL to the main website for this project.
|
||||
# ProjectUri = ''
|
||||
|
||||
# A URL to an icon representing this module.
|
||||
# IconUri = ''
|
||||
|
||||
# ReleaseNotes of this module
|
||||
# ReleaseNotes = ''
|
||||
|
||||
# Prerelease string of this module
|
||||
# Prerelease = ''
|
||||
|
||||
# Flag to indicate whether the module requires explicit user acceptance for install/update/save
|
||||
# RequireLicenseAcceptance = $false
|
||||
|
||||
# External dependent modules of this module
|
||||
# ExternalModuleDependencies = @()
|
||||
|
||||
} # End of PSData hashtable
|
||||
|
||||
} # End of PrivateData hashtable
|
||||
|
||||
}
|
||||
|
||||
|
||||
""";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
// 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.Linq;
|
||||
using static PowerToys.Settings.DSC.Schema.Introspection;
|
||||
|
||||
namespace PowerToys.Settings.DSC.Schema;
|
||||
|
||||
internal sealed class DocumentationGeneration
|
||||
{
|
||||
private static readonly string IsAvailableSymbol = "✅";
|
||||
private static readonly string IsUnavailableSymbol = "❌";
|
||||
private static readonly string MissingValueIndicator = "—";
|
||||
|
||||
private static readonly string PropertySuffix = "Property";
|
||||
|
||||
private static string SimplifyPropertyType(string typeName)
|
||||
{
|
||||
if (typeName.EndsWith(PropertySuffix, StringComparison.InvariantCulture))
|
||||
{
|
||||
typeName = typeName.Remove(typeName.LastIndexOf(PropertySuffix, StringComparison.InvariantCulture), PropertySuffix.Length);
|
||||
}
|
||||
|
||||
return typeName;
|
||||
}
|
||||
|
||||
private static string EmitPropertyTableLine(string name, ModulePropertyStructure info)
|
||||
{
|
||||
bool isAvailable = !info.IsIgnored;
|
||||
var availabilitySymbol = isAvailable ? IsAvailableSymbol : IsUnavailableSymbol;
|
||||
var documentation = MissingValueIndicator;
|
||||
if (info.Type.IsEnum)
|
||||
{
|
||||
documentation = "Possible values: ";
|
||||
foreach (var enumValue in Enum.GetValues(info.Type))
|
||||
{
|
||||
documentation += enumValue.ToString() + ' ';
|
||||
}
|
||||
}
|
||||
|
||||
var propertyType = isAvailable ? SimplifyPropertyType(info.Type.Name) : MissingValueIndicator;
|
||||
return $"| {name} | {propertyType} | {documentation} | {availabilitySymbol} |";
|
||||
}
|
||||
|
||||
private static string EmitModulePropertiesTable(SettingsStructure module)
|
||||
{
|
||||
bool generalSettings = module.Name == "GeneralSettings";
|
||||
|
||||
var properties = module.Properties
|
||||
.Where(p => !p.Value.IsIgnoredByJsonSerializer)
|
||||
.Select(property => EmitPropertyTableLine(property.Key, property.Value)).Aggregate((acc, line) => string.Join(Environment.NewLine, [acc, line]));
|
||||
|
||||
var propertyDefinitionsBlock = string.Empty;
|
||||
var applyChangesBlock = string.Empty;
|
||||
return $$"""
|
||||
### {{module.Name}}
|
||||
|
||||
| Name | Type | Description | Available |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
{{properties}}
|
||||
|
||||
|
||||
""";
|
||||
}
|
||||
|
||||
public static string EmitDocumentationFileContents(SettingsStructure[] moduleSettings, SettingsStructure generalSettings)
|
||||
{
|
||||
var moduleTables = string.Empty;
|
||||
|
||||
foreach (var module in moduleSettings.Append(generalSettings))
|
||||
{
|
||||
moduleTables += EmitModulePropertiesTable(module);
|
||||
}
|
||||
|
||||
return moduleTables;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
// 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.Reflection;
|
||||
using System.Text.Json.Serialization;
|
||||
using Settings.UI.Library.Attributes;
|
||||
|
||||
namespace PowerToys.Settings.DSC.Schema;
|
||||
|
||||
public class Introspection
|
||||
{
|
||||
public struct ModulePropertyStructure
|
||||
{
|
||||
public bool IsIgnoredByJsonSerializer;
|
||||
public bool IsIgnoredByCmdConfigureAttribute;
|
||||
|
||||
public bool IsIgnored
|
||||
{
|
||||
get { return IsIgnoredByJsonSerializer || IsIgnoredByCmdConfigureAttribute; }
|
||||
}
|
||||
|
||||
public Type Type;
|
||||
}
|
||||
|
||||
public struct SettingsStructure
|
||||
{
|
||||
public string Name;
|
||||
public Dictionary<string, ModulePropertyStructure> Properties;
|
||||
}
|
||||
|
||||
private static bool IsModuleNameField(FieldInfo info)
|
||||
{
|
||||
return info != null && info.IsLiteral && !info.IsInitOnly
|
||||
&& info.FieldType == typeof(string);
|
||||
}
|
||||
|
||||
private static bool IsSettingsClassType(Type type)
|
||||
{
|
||||
return type.IsClass && type.FullName.EndsWith("Settings", StringComparison.InvariantCulture);
|
||||
}
|
||||
|
||||
private static Dictionary<string, ModulePropertyStructure> ParseProperties(Type propertiesType)
|
||||
{
|
||||
return propertiesType.GetProperties().Select(property =>
|
||||
{
|
||||
var jsonIgnoreAttr = property.GetCustomAttribute<JsonIgnoreAttribute>();
|
||||
var cmdIgnoreAttr = property.GetCustomAttribute<CmdConfigureIgnoreAttribute>();
|
||||
|
||||
return (property.Name, new ModulePropertyStructure
|
||||
{
|
||||
Type = property.PropertyType,
|
||||
IsIgnoredByJsonSerializer = jsonIgnoreAttr != null,
|
||||
IsIgnoredByCmdConfigureAttribute = cmdIgnoreAttr != null,
|
||||
});
|
||||
}).ToDictionary();
|
||||
}
|
||||
|
||||
public static SettingsStructure ParseGeneralSettings(Assembly assembly)
|
||||
{
|
||||
return assembly
|
||||
.GetTypes()
|
||||
.Where(IsSettingsClassType)
|
||||
.Where(type => type.Name == "GeneralSettings")
|
||||
.Select(type => new SettingsStructure
|
||||
{
|
||||
Name = type.Name,
|
||||
Properties = ParseProperties(type),
|
||||
}).FirstOrDefault();
|
||||
}
|
||||
|
||||
public static SettingsStructure[] ParseModuleSettings(Assembly assembly)
|
||||
{
|
||||
return assembly
|
||||
.GetTypes()
|
||||
.Where(IsSettingsClassType)
|
||||
.Select(type => new
|
||||
{
|
||||
Properties = type.GetProperty("Properties"),
|
||||
ModuleNameInfo = type.GetField("ModuleName", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy),
|
||||
TypeName = type.Name,
|
||||
})
|
||||
.Where(x => x.Properties?.PropertyType.IsClass == true && IsModuleNameField(x.ModuleNameInfo))
|
||||
.Select(x => new SettingsStructure
|
||||
{
|
||||
Name = x.TypeName.Replace("Settings", string.Empty),
|
||||
Properties = ParseProperties(x.Properties.PropertyType),
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\Version.props" />
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<RootNamespace>PowerToys.Settings.DSC.Schema</RootNamespace>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<RuntimeIdentifiers>win-x64;win-arm64</RuntimeIdentifiers>
|
||||
<WindowsPackageType>None</WindowsPackageType>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<SelfContained>true</SelfContained>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- SelfContained=true requires RuntimeIdentifier to be set -->
|
||||
<PropertyGroup Condition="'$(Platform)'=='x64'">
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Platform)'=='ARM64'">
|
||||
<RuntimeIdentifier>win-arm64</RuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<NoWarn></NoWarn>
|
||||
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
|
||||
<WarningsNotAsErrors>CA1720</WarningsNotAsErrors>
|
||||
<Optimize>False</Optimize>
|
||||
<DebugType>full</DebugType>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'">
|
||||
<NoWarn></NoWarn>
|
||||
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
|
||||
<WarningsNotAsErrors>CA1720</WarningsNotAsErrors>
|
||||
<Optimize>true</Optimize>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- See https://learn.microsoft.com/windows/apps/develop/platform/csharp-winrt/net-projection-from-cppwinrt-component for more info -->
|
||||
<PropertyGroup>
|
||||
<CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir>
|
||||
<ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<GeneratedDSCModule>"$(ProjectDir)..\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\$(Version)\Microsoft.PowerToys.Configure.psm1"</GeneratedDSCModule>
|
||||
<GeneratedDSCManifest>"$(ProjectDir)..\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\$(Version)\Microsoft.PowerToys.Configure.psd1"</GeneratedDSCManifest>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- The following sections assume that the machine we're building on is always x64. That means we won't be able to run/inspect arm64 executables, therefore we must always execute x64 generator. -->
|
||||
|
||||
<Target Name="PostBuildAction" AfterTargets="Build" Outputs="$(GeneratedDSCModule)" Condition="'$(Platform)'!='ARM64'">
|
||||
<Exec Command=""$(OutDir)$(AssemblyName).exe" "$(SolutionDir)x64\$(Configuration)\WinUI3Apps\PowerToys.Settings.UI.Lib.dll" $(GeneratedDSCModule) $(GeneratedDSCManifest)" />
|
||||
</Target>
|
||||
|
||||
<Target Name="PreBuild" BeforeTargets="PreBuildEvent" Condition="'$(Platform)'=='ARM64'">
|
||||
<Exec Command=""$(MSBuildToolsPath)\msbuild.exe" PowerToys.sln -p:Configuration="$(Configuration)" -p:Platform="x64" -verbosity:m -t:DSC\PowerToys_Settings_DSC_Schema_Generator" WorkingDirectory="$(SolutionDir)" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
102
src/dsc/PowerToys.Settings.DSC.Schema.Generator/Program.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
// 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.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace PowerToys.Settings.DSC.Schema;
|
||||
|
||||
internal sealed class Program
|
||||
{
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
if (args.Length < 2)
|
||||
{
|
||||
Console.WriteLine("Usage: Generator.exe <PowerToys.Settings.UI.Lib.dll path> <module output path> <manifest output path>");
|
||||
return 1;
|
||||
}
|
||||
|
||||
var dllPath = args[0];
|
||||
var moduleOutputPath = args[1];
|
||||
var manifestOutputPath = string.Empty;
|
||||
|
||||
bool documentationMode = Path.GetExtension(moduleOutputPath) == ".md";
|
||||
bool sampleMode = Path.GetExtension(moduleOutputPath) == ".yaml";
|
||||
|
||||
if (!documentationMode && !sampleMode)
|
||||
{
|
||||
if (args.Length < 3)
|
||||
{
|
||||
Console.WriteLine("Usage: Generator.exe <PowerToys.Settings.UI.Lib.dll path> <module output path> <manifest output path>");
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
manifestOutputPath = args[2];
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(moduleOutputPath));
|
||||
|
||||
var assembly = Assembly.LoadFrom(dllPath);
|
||||
var moduleSettings = Introspection.ParseModuleSettings(assembly);
|
||||
var generalSettings = Introspection.ParseGeneralSettings(assembly);
|
||||
#if DEBUG
|
||||
PrintUniquePropertyTypes(moduleSettings);
|
||||
#endif
|
||||
var outputFileContents = string.Empty;
|
||||
if (documentationMode)
|
||||
{
|
||||
outputFileContents = DocumentationGeneration.EmitDocumentationFileContents(moduleSettings, generalSettings);
|
||||
}
|
||||
else if (sampleMode)
|
||||
{
|
||||
outputFileContents = SampleGeneration.EmitSampleFileContents(moduleSettings, generalSettings);
|
||||
}
|
||||
else
|
||||
{
|
||||
var manifestFileContents = DSCGeneration.EmitManifestFileContents();
|
||||
File.WriteAllText(manifestOutputPath, manifestFileContents);
|
||||
var debugSettingsPath = Path.Combine(Directory.GetParent(dllPath).FullName, "PowerToys.Settings.exe");
|
||||
outputFileContents = DSCGeneration.EmitModuleFileContents(moduleSettings, generalSettings, debugSettingsPath);
|
||||
}
|
||||
|
||||
File.WriteAllText(moduleOutputPath, outputFileContents);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error: {ex.Message}");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void PrintUniquePropertyTypes(Introspection.SettingsStructure[] moduleSettings)
|
||||
{
|
||||
Console.WriteLine("Detected the following module properties types:");
|
||||
var propertyTypes = new HashSet<Type>();
|
||||
foreach (var settings in moduleSettings)
|
||||
{
|
||||
Console.WriteLine($"{settings.Name}");
|
||||
foreach (var (_, property) in settings.Properties)
|
||||
{
|
||||
if (!property.IsIgnored)
|
||||
{
|
||||
propertyTypes.Add(property.Type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine("\nDetected the following unique property types:");
|
||||
foreach (var type in propertyTypes)
|
||||
{
|
||||
Console.WriteLine($"{type}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
// 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.Globalization;
|
||||
using System.Linq;
|
||||
using static PowerToys.Settings.DSC.Schema.Introspection;
|
||||
|
||||
namespace PowerToys.Settings.DSC.Schema;
|
||||
|
||||
internal sealed class SampleGeneration
|
||||
{
|
||||
private const int FixedSeed = 12345;
|
||||
private static readonly Random _random = new Random(FixedSeed);
|
||||
|
||||
private static string EmitPropertySetter(string name, ModulePropertyStructure info)
|
||||
{
|
||||
var randomPropertyValue = "\"<string>\"";
|
||||
if (Common.InferIsBool(info.Type))
|
||||
{
|
||||
randomPropertyValue = _random.Next(2) == 1 ? "true" : "false";
|
||||
}
|
||||
else if (Common.InferIsInt(info.Type))
|
||||
{
|
||||
randomPropertyValue = _random.Next(256).ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
else if (info.Type.IsEnum)
|
||||
{
|
||||
var enumValues = Enum.GetValues(info.Type);
|
||||
randomPropertyValue = enumValues.GetValue(_random.Next(enumValues.Length)).ToString();
|
||||
}
|
||||
|
||||
return $" {name}: {randomPropertyValue}";
|
||||
}
|
||||
|
||||
private static string EmitModulePropertiesSection(SettingsStructure module)
|
||||
{
|
||||
bool generalSettings = module.Name == "GeneralSettings";
|
||||
|
||||
var propertiesCollection = module.Properties
|
||||
.Where(p => !p.Value.IsIgnored)
|
||||
.Select(property => EmitPropertySetter(property.Key, property.Value))
|
||||
.ToList();
|
||||
|
||||
string properties = propertiesCollection.Count != 0
|
||||
? propertiesCollection.Aggregate((acc, line) => string.Join(Environment.NewLine, acc, line))
|
||||
: string.Empty;
|
||||
|
||||
var propertyDefinitionsBlock = string.Empty;
|
||||
var applyChangesBlock = string.Empty;
|
||||
return $$"""
|
||||
{{module.Name}}:
|
||||
{{properties}}
|
||||
|
||||
|
||||
""";
|
||||
}
|
||||
|
||||
public static string EmitSampleFileContents(SettingsStructure[] moduleSettings, SettingsStructure generalSettings)
|
||||
{
|
||||
var moduleTables = $$"""
|
||||
properties:
|
||||
resources:
|
||||
- resource: PowerToysConfigure
|
||||
directives:
|
||||
description: Configure PowerToys
|
||||
settings:
|
||||
|
||||
""";
|
||||
|
||||
foreach (var module in moduleSettings.Append(generalSettings))
|
||||
{
|
||||
moduleTables += EmitModulePropertiesSection(module);
|
||||
}
|
||||
|
||||
moduleTables += " configurationVersion: 0.2.0";
|
||||
return moduleTables;
|
||||
}
|
||||
}
|
||||
21
src/dsc/PowerToys.Settings.DSC.Schema.Generator/app.manifest
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="PowerToys.Settings.app"/>
|
||||
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<!-- The combination of below two tags have the following effect:
|
||||
1) Per-Monitor for >= Windows 10 Anniversary Update
|
||||
2) System < Windows 10 Anniversary Update
|
||||
-->
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- Windows 10 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||
</application>
|
||||
</compatibility>
|
||||
</assembly>
|
||||
@@ -90,14 +90,12 @@ public:
|
||||
}
|
||||
|
||||
m_enabled = true;
|
||||
save_settings();
|
||||
}
|
||||
|
||||
virtual void disable() override
|
||||
{
|
||||
Logger::info(L"File Locksmith disabled");
|
||||
m_enabled = false;
|
||||
save_settings();
|
||||
}
|
||||
|
||||
virtual bool is_enabled() override
|
||||
@@ -123,7 +121,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_enabled;
|
||||
bool m_enabled = false;
|
||||
bool m_extended_only;
|
||||
|
||||
void init_settings()
|
||||
@@ -136,7 +134,7 @@ private:
|
||||
void save_settings()
|
||||
{
|
||||
auto& settings = FileLocksmithSettingsInstance();
|
||||
settings.SetEnabled(m_enabled);
|
||||
m_enabled = FileLocksmithSettingsInstance().GetEnabled();
|
||||
settings.SetExtendedContextMenuOnly(m_extended_only);
|
||||
|
||||
settings.Save();
|
||||
|
||||
@@ -18,10 +18,12 @@ static bool LastModifiedTime(const std::wstring& filePath, FILETIME* lpFileTime)
|
||||
|
||||
FileLocksmithSettings::FileLocksmithSettings()
|
||||
{
|
||||
generalJsonFilePath = PTSettingsHelper::get_powertoys_general_save_file_location();
|
||||
std::wstring savePath = PTSettingsHelper::get_module_save_folder_location(constants::nonlocalizable::PowerToyKey);
|
||||
std::error_code ec;
|
||||
|
||||
jsonFilePath = savePath + constants::nonlocalizable::DataFilePath;
|
||||
RefreshEnabledState();
|
||||
Load();
|
||||
}
|
||||
|
||||
@@ -29,7 +31,6 @@ void FileLocksmithSettings::Save()
|
||||
{
|
||||
json::JsonObject jsonData;
|
||||
|
||||
jsonData.SetNamedValue(constants::nonlocalizable::JsonKeyEnabled, json::value(settings.enabled));
|
||||
jsonData.SetNamedValue(constants::nonlocalizable::JsonKeyShowInExtendedContextMenu, json::value(settings.showInExtendedContextMenu));
|
||||
|
||||
json::to_file(jsonFilePath, jsonData);
|
||||
@@ -48,6 +49,32 @@ void FileLocksmithSettings::Load()
|
||||
}
|
||||
}
|
||||
|
||||
void FileLocksmithSettings::RefreshEnabledState()
|
||||
{
|
||||
// Load json settings from data file if it is modified in the meantime.
|
||||
FILETIME lastModifiedTime{};
|
||||
if (!(LastModifiedTime(generalJsonFilePath, &lastModifiedTime) &&
|
||||
CompareFileTime(&lastModifiedTime, &lastLoadedGeneralSettingsTime) == 1))
|
||||
return;
|
||||
|
||||
lastLoadedGeneralSettingsTime = lastModifiedTime;
|
||||
|
||||
auto json = json::from_file(generalJsonFilePath);
|
||||
if (!json)
|
||||
return;
|
||||
|
||||
const json::JsonObject& jsonSettings = json.value();
|
||||
try
|
||||
{
|
||||
json::JsonObject modulesEnabledState;
|
||||
json::get(jsonSettings, L"enabled", modulesEnabledState, json::JsonObject{});
|
||||
json::get(modulesEnabledState, L"File Locksmith", settings.enabled, true);
|
||||
}
|
||||
catch (const winrt::hresult_error&)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
void FileLocksmithSettings::Reload()
|
||||
{
|
||||
// Load json settings from data file if it is modified in the meantime.
|
||||
@@ -67,11 +94,6 @@ void FileLocksmithSettings::ParseJson()
|
||||
const json::JsonObject& jsonSettings = json.value();
|
||||
try
|
||||
{
|
||||
if (json::has(jsonSettings, constants::nonlocalizable::JsonKeyEnabled, json::JsonValueType::Boolean))
|
||||
{
|
||||
settings.enabled = jsonSettings.GetNamedBoolean(constants::nonlocalizable::JsonKeyEnabled);
|
||||
}
|
||||
|
||||
if (json::has(jsonSettings, constants::nonlocalizable::JsonKeyShowInExtendedContextMenu, json::JsonValueType::Boolean))
|
||||
{
|
||||
settings.showInExtendedContextMenu = jsonSettings.GetNamedBoolean(constants::nonlocalizable::JsonKeyShowInExtendedContextMenu);
|
||||
|
||||
@@ -16,15 +16,10 @@ public:
|
||||
if (gpoSetting == powertoys_gpo::gpo_rule_configured_disabled)
|
||||
return false;
|
||||
Reload();
|
||||
RefreshEnabledState();
|
||||
return settings.enabled;
|
||||
}
|
||||
|
||||
inline void SetEnabled(bool enabled)
|
||||
{
|
||||
settings.enabled = enabled;
|
||||
Save();
|
||||
}
|
||||
|
||||
inline bool GetShowInExtendedContextMenu() const
|
||||
{
|
||||
return settings.showInExtendedContextMenu;
|
||||
@@ -45,12 +40,15 @@ private:
|
||||
bool showInExtendedContextMenu{ false };
|
||||
};
|
||||
|
||||
void RefreshEnabledState();
|
||||
void Reload();
|
||||
void ParseJson();
|
||||
|
||||
Settings settings;
|
||||
std::wstring generalJsonFilePath;
|
||||
std::wstring jsonFilePath;
|
||||
FILETIME lastLoadedTime;
|
||||
FILETIME lastLoadedTime{};
|
||||
FILETIME lastLoadedGeneralSettingsTime{};
|
||||
};
|
||||
|
||||
FileLocksmithSettings& FileLocksmithSettingsInstance();
|
||||
|
||||
@@ -26,6 +26,7 @@ using Microsoft.PowerToys.Settings.UI.Library.Utilities;
|
||||
// 2023- Included in PowerToys.
|
||||
// </history>
|
||||
using Microsoft.Win32;
|
||||
using Settings.UI.Library.Attributes;
|
||||
|
||||
[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Properties.Setting.Values.#LoadIntSetting(System.String,System.Int32)", Justification = "Dotnet port with style preservation")]
|
||||
[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Properties.Setting.Values.#SaveSetting(System.String,System.Object)", Justification = "Dotnet port with style preservation")]
|
||||
@@ -766,6 +767,7 @@ namespace MouseWithoutBorders.Class
|
||||
}
|
||||
}
|
||||
|
||||
[CmdConfigureIgnore]
|
||||
internal bool DrawMouseEx
|
||||
{
|
||||
get
|
||||
|
||||
@@ -47,51 +47,17 @@ internal sealed class ImageMethods
|
||||
return destination;
|
||||
}
|
||||
|
||||
internal static ImageSource GetWindowBoundsImage(Window passedWindow)
|
||||
internal static ImageSource GetWindowBoundsImage(OCROverlay passedWindow)
|
||||
{
|
||||
DpiScale dpi = VisualTreeHelper.GetDpi(passedWindow);
|
||||
int windowWidth = (int)(passedWindow.ActualWidth * dpi.DpiScaleX);
|
||||
int windowHeight = (int)(passedWindow.ActualHeight * dpi.DpiScaleY);
|
||||
|
||||
System.Windows.Point absPosPoint = passedWindow.GetAbsolutePosition();
|
||||
int thisCorrectedLeft = (int)absPosPoint.X;
|
||||
int thisCorrectedTop = (int)absPosPoint.Y;
|
||||
|
||||
using Bitmap bmp = new(windowWidth, windowHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
||||
Rectangle screenRectangle = passedWindow.GetScreenRectangle();
|
||||
using Bitmap bmp = new(screenRectangle.Width, screenRectangle.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
||||
using Graphics g = Graphics.FromImage(bmp);
|
||||
|
||||
g.CopyFromScreen(thisCorrectedLeft, thisCorrectedTop, 0, 0, bmp.Size, CopyPixelOperation.SourceCopy);
|
||||
g.CopyFromScreen(screenRectangle.Left, screenRectangle.Top, 0, 0, bmp.Size, CopyPixelOperation.SourceCopy);
|
||||
return BitmapToImageSource(bmp);
|
||||
}
|
||||
|
||||
internal static Bitmap GetWindowBoundsBitmap(Window passedWindow)
|
||||
{
|
||||
DpiScale dpi = VisualTreeHelper.GetDpi(passedWindow);
|
||||
int windowWidth = (int)(passedWindow.ActualWidth * dpi.DpiScaleX);
|
||||
int windowHeight = (int)(passedWindow.ActualHeight * dpi.DpiScaleY);
|
||||
|
||||
System.Windows.Point absPosPoint = passedWindow.GetAbsolutePosition();
|
||||
int thisCorrectedLeft = (int)absPosPoint.X;
|
||||
int thisCorrectedTop = (int)absPosPoint.Y;
|
||||
|
||||
Bitmap bmp = new(
|
||||
windowWidth,
|
||||
windowHeight,
|
||||
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
||||
using Graphics g = Graphics.FromImage(bmp);
|
||||
|
||||
g.CopyFromScreen(
|
||||
thisCorrectedLeft,
|
||||
thisCorrectedTop,
|
||||
0,
|
||||
0,
|
||||
bmp.Size,
|
||||
CopyPixelOperation.SourceCopy);
|
||||
|
||||
return bmp;
|
||||
}
|
||||
|
||||
internal static Bitmap GetRegionAsBitmap(Window passedWindow, Rectangle selectedRegion)
|
||||
internal static Bitmap GetRegionAsBitmap(OCROverlay passedWindow, Rectangle selectedRegion)
|
||||
{
|
||||
Bitmap bmp = new(
|
||||
selectedRegion.Width,
|
||||
@@ -99,15 +65,11 @@ internal sealed class ImageMethods
|
||||
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
||||
|
||||
using Graphics g = Graphics.FromImage(bmp);
|
||||
|
||||
System.Windows.Point absPosPoint = passedWindow.GetAbsolutePosition();
|
||||
|
||||
int thisCorrectedLeft = (int)absPosPoint.X + selectedRegion.Left;
|
||||
int thisCorrectedTop = (int)absPosPoint.Y + selectedRegion.Top;
|
||||
Rectangle screenRectangle = passedWindow.GetScreenRectangle();
|
||||
|
||||
g.CopyFromScreen(
|
||||
thisCorrectedLeft,
|
||||
thisCorrectedTop,
|
||||
screenRectangle.Left + selectedRegion.Left,
|
||||
screenRectangle.Top + selectedRegion.Top,
|
||||
0,
|
||||
0,
|
||||
bmp.Size,
|
||||
@@ -117,7 +79,7 @@ internal sealed class ImageMethods
|
||||
return bmp;
|
||||
}
|
||||
|
||||
internal static async Task<string> GetRegionsText(Window? passedWindow, Rectangle selectedRegion, Language? preferredLanguage)
|
||||
internal static async Task<string> GetRegionsText(OCROverlay? passedWindow, Rectangle selectedRegion, Language? preferredLanguage)
|
||||
{
|
||||
if (passedWindow is null)
|
||||
{
|
||||
@@ -130,17 +92,15 @@ internal sealed class ImageMethods
|
||||
return resultText != null ? resultText.Trim() : string.Empty;
|
||||
}
|
||||
|
||||
internal static async Task<string> GetClickedWord(Window passedWindow, System.Windows.Point clickedPoint, Language? preferredLanguage)
|
||||
internal static async Task<string> GetClickedWord(OCROverlay passedWindow, System.Windows.Point clickedPoint, Language? preferredLanguage)
|
||||
{
|
||||
DpiScale dpi = VisualTreeHelper.GetDpi(passedWindow);
|
||||
Bitmap bmp = new((int)(passedWindow.ActualWidth * dpi.DpiScaleX), (int)(passedWindow.ActualHeight * dpi.DpiScaleY), System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
||||
Rectangle screenRectangle = passedWindow.GetScreenRectangle();
|
||||
Bitmap bmp = new((int)screenRectangle.Width, (int)passedWindow.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
||||
Graphics g = Graphics.FromImage(bmp);
|
||||
|
||||
System.Windows.Point absPosPoint = passedWindow.GetAbsolutePosition();
|
||||
int thisCorrectedLeft = (int)absPosPoint.X;
|
||||
int thisCorrectedTop = (int)absPosPoint.Y;
|
||||
|
||||
g.CopyFromScreen(thisCorrectedLeft, thisCorrectedTop, 0, 0, bmp.Size, CopyPixelOperation.SourceCopy);
|
||||
g.CopyFromScreen((int)absPosPoint.X, (int)absPosPoint.Y, 0, 0, bmp.Size, CopyPixelOperation.SourceCopy);
|
||||
|
||||
System.Windows.Point adjustedPoint = new(clickedPoint.X, clickedPoint.Y);
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace PowerOCR.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<string> GetRegionsTextAsTableAsync(Window passedWindow, Rectangle regionScaled, Language? language)
|
||||
public static async Task<string> GetRegionsTextAsTableAsync(OCROverlay passedWindow, Rectangle regionScaled, Language? language)
|
||||
{
|
||||
if (language is null)
|
||||
{
|
||||
|
||||
@@ -37,4 +37,28 @@ public static class WPFExtensionMethods
|
||||
|
||||
return new Point(r.X, r.Y);
|
||||
}
|
||||
|
||||
public static DpiScale GetDpi(this System.Windows.Forms.Screen screen)
|
||||
{
|
||||
var point = new System.Drawing.Point(screen.Bounds.Left + 1, screen.Bounds.Top + 1);
|
||||
var mon = MonitorFromPoint(point, 2/*MONITOR_DEFAULTTONEAREST*/);
|
||||
GetDpiForMonitor(mon, DpiType.Effective, out uint dpiX, out uint dpiY);
|
||||
return new DpiScale(dpiX / 96.0, dpiY / 96.0);
|
||||
}
|
||||
|
||||
// https://msdn.microsoft.com/library/windows/desktop/dd145062(v=vs.85).aspx
|
||||
[DllImport("User32.dll")]
|
||||
private static extern IntPtr MonitorFromPoint([In] System.Drawing.Point pt, [In] uint dwFlags);
|
||||
|
||||
// https://msdn.microsoft.com/library/windows/desktop/dn280510(v=vs.85).aspx
|
||||
[DllImport("Shcore.dll")]
|
||||
private static extern IntPtr GetDpiForMonitor([In] IntPtr hmonitor, [In] DpiType dpiType, [Out] out uint dpiX, [Out] out uint dpiY);
|
||||
|
||||
// https://msdn.microsoft.com/library/windows/desktop/dn280511(v=vs.85).aspx
|
||||
public enum DpiType
|
||||
{
|
||||
Effective = 0,
|
||||
Angular = 1,
|
||||
Raw = 2,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,9 @@ public static class WindowUtilities
|
||||
Logger.LogInfo($"Adding Overlays for each screen");
|
||||
foreach (Screen screen in Screen.AllScreens)
|
||||
{
|
||||
Logger.LogInfo($"screen {screen}");
|
||||
OCROverlay overlay = new(screen.Bounds);
|
||||
DpiScale dpiScale = screen.GetDpi();
|
||||
Logger.LogInfo($"screen {screen}, dpiScale {dpiScale.DpiScaleX}, {dpiScale.DpiScaleY}");
|
||||
OCROverlay overlay = new(screen.Bounds, dpiScale);
|
||||
|
||||
overlay.Show();
|
||||
ActivateWindow(overlay);
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
xmlns:p="clr-namespace:PowerOCR.Properties"
|
||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||
Title="TextExtractor"
|
||||
Width="200"
|
||||
Height="200"
|
||||
ui:Design.Background="Transparent"
|
||||
AllowsTransparency="True"
|
||||
Background="Transparent"
|
||||
|
||||
@@ -5,10 +5,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
using Common.UI;
|
||||
using ManagedCommon;
|
||||
@@ -42,11 +44,21 @@ public partial class OCROverlay : Window
|
||||
private bool isComboBoxReady;
|
||||
private const double ActiveOpacity = 0.4;
|
||||
private readonly UserSettings userSettings = new(new ThrottledActionInvoker());
|
||||
private System.Drawing.Rectangle screenRectangle;
|
||||
private DpiScale dpiScale;
|
||||
|
||||
public OCROverlay(System.Drawing.Rectangle screenRectangle)
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
private static extern bool MoveWindow(IntPtr hWnd, int x, int y, int nWidth, int nHeight, bool bRepaint);
|
||||
|
||||
public OCROverlay(System.Drawing.Rectangle screenRectangleParam, DpiScale dpiScaleParam)
|
||||
{
|
||||
Left = screenRectangle.Left >= 0 ? screenRectangle.Left : screenRectangle.Left + (screenRectangle.Width / 2);
|
||||
Top = screenRectangle.Top >= 0 ? screenRectangle.Top : screenRectangle.Top + (screenRectangle.Height / 2);
|
||||
screenRectangle = screenRectangleParam;
|
||||
dpiScale = dpiScaleParam;
|
||||
|
||||
Left = screenRectangle.Left;
|
||||
Top = screenRectangle.Top;
|
||||
Width = screenRectangle.Width / dpiScale.DpiScaleX;
|
||||
Height = screenRectangle.Height / dpiScale.DpiScaleY;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
@@ -106,7 +118,6 @@ public partial class OCROverlay : Window
|
||||
|
||||
private void Window_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
WindowState = WindowState.Maximized;
|
||||
FullWindow.Rect = new Rect(0, 0, Width, Height);
|
||||
KeyDown += MainWindow_KeyDown;
|
||||
KeyUp += MainWindow_KeyUp;
|
||||
@@ -119,6 +130,12 @@ public partial class OCROverlay : Window
|
||||
#if DEBUG
|
||||
Topmost = false;
|
||||
#endif
|
||||
IntPtr hwnd = new WindowInteropHelper(this).Handle;
|
||||
|
||||
// The first move puts it on the correct monitor, which triggers WM_DPICHANGED
|
||||
// The +1/-1 coerces WPF to update Window.Top/Left/Width/Height in the second move
|
||||
MoveWindow(hwnd, (int)(screenRectangle.Left + 1), (int)screenRectangle.Top, (int)(screenRectangle.Width - 1), (int)screenRectangle.Height, false);
|
||||
MoveWindow(hwnd, (int)screenRectangle.Left, (int)screenRectangle.Top, (int)screenRectangle.Width, (int)screenRectangle.Height, true);
|
||||
}
|
||||
|
||||
private void Window_Unloaded(object sender, RoutedEventArgs e)
|
||||
@@ -476,4 +493,9 @@ public partial class OCROverlay : Window
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public System.Drawing.Rectangle GetScreenRectangle()
|
||||
{
|
||||
return screenRectangle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,12 @@
|
||||
<!-- Windows 10 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||
|
||||
<windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
|
||||
PerMonitor
|
||||
</dpiAwareness>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
</compatibility>
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 178 KiB After Width: | Height: | Size: 178 KiB |
|
Before Width: | Height: | Size: 179 KiB After Width: | Height: | Size: 179 KiB |
@@ -1,4 +1,4 @@
|
||||
<ui:FluentWindow
|
||||
<Window
|
||||
x:Class="ColorPicker.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
@@ -6,21 +6,19 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:e="http://schemas.microsoft.com/xaml/behaviors"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||
Width="120"
|
||||
Height="64"
|
||||
MinWidth="0"
|
||||
MinHeight="0"
|
||||
AllowsTransparency="True"
|
||||
AutomationProperties.Name="Color Picker"
|
||||
Background="Transparent"
|
||||
ExtendsContentIntoTitleBar="True"
|
||||
Opacity="0.1"
|
||||
ResizeMode="NoResize"
|
||||
ShowInTaskbar="False"
|
||||
SizeToContent="WidthAndHeight"
|
||||
SourceInitialized="MainWindowSourceInitialized"
|
||||
Topmost="True"
|
||||
WindowCornerPreference="Default"
|
||||
WindowStyle="None"
|
||||
mc:Ignorable="d">
|
||||
<e:Interaction.Behaviors>
|
||||
@@ -28,4 +26,4 @@
|
||||
<behaviors:AppearAnimationBehavior />
|
||||
</e:Interaction.Behaviors>
|
||||
<ContentControl x:Name="MainView" Content="{Binding MainViewModel}" />
|
||||
</ui:FluentWindow>
|
||||
</Window>
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace ColorPicker
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class MainWindow : FluentWindow
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public MainWindow()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FancyZonesEditorCommon.Data
|
||||
{
|
||||
public class AppliedLayouts : EditorData<AppliedLayouts.AppliedLayoutsListWrapper>
|
||||
{
|
||||
public string File
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetDataFolder() + "\\Microsoft\\PowerToys\\FancyZones\\applied-layouts.json";
|
||||
}
|
||||
}
|
||||
|
||||
public struct AppliedLayoutWrapper
|
||||
{
|
||||
public struct DeviceIdWrapper
|
||||
{
|
||||
public string Monitor { get; set; }
|
||||
|
||||
public string MonitorInstance { get; set; }
|
||||
|
||||
public int MonitorNumber { get; set; }
|
||||
|
||||
public string SerialNumber { get; set; }
|
||||
|
||||
public string VirtualDesktop { get; set; }
|
||||
}
|
||||
|
||||
public struct LayoutWrapper
|
||||
{
|
||||
public string Uuid { get; set; }
|
||||
|
||||
public string Type { get; set; }
|
||||
|
||||
public bool ShowSpacing { get; set; }
|
||||
|
||||
public int Spacing { get; set; }
|
||||
|
||||
public int ZoneCount { get; set; }
|
||||
|
||||
public int SensitivityRadius { get; set; }
|
||||
}
|
||||
|
||||
public DeviceIdWrapper Device { get; set; }
|
||||
|
||||
public LayoutWrapper AppliedLayout { get; set; }
|
||||
}
|
||||
|
||||
public struct AppliedLayoutsListWrapper
|
||||
{
|
||||
public List<AppliedLayoutWrapper> AppliedLayouts { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace FancyZonesEditorCommon.Data
|
||||
{
|
||||
public static class Constants
|
||||
{
|
||||
public enum TemplateLayout
|
||||
{
|
||||
Empty,
|
||||
Focus,
|
||||
Rows,
|
||||
Columns,
|
||||
Grid,
|
||||
PriorityGrid,
|
||||
}
|
||||
|
||||
public static readonly ReadOnlyDictionary<TemplateLayout, string> TemplateLayoutJsonTags = new ReadOnlyDictionary<TemplateLayout, string>(
|
||||
new Dictionary<TemplateLayout, string>()
|
||||
{
|
||||
{ TemplateLayout.Empty, "blank" },
|
||||
{ TemplateLayout.Focus, "focus" },
|
||||
{ TemplateLayout.Rows, "rows" },
|
||||
{ TemplateLayout.Columns, "columns" },
|
||||
{ TemplateLayout.Grid, "grid" },
|
||||
{ TemplateLayout.PriorityGrid, "priority-grid" },
|
||||
});
|
||||
|
||||
public const string CustomLayoutJsonTag = "custom";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using static FancyZonesEditorCommon.Data.CustomLayouts;
|
||||
|
||||
namespace FancyZonesEditorCommon.Data
|
||||
{
|
||||
public class CustomLayouts : EditorData<CustomLayoutListWrapper>
|
||||
{
|
||||
public string File
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetDataFolder() + "\\Microsoft\\PowerToys\\FancyZones\\custom-layouts.json";
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class CanvasInfoWrapper
|
||||
{
|
||||
public struct CanvasZoneWrapper
|
||||
{
|
||||
public int X { get; set; }
|
||||
|
||||
public int Y { get; set; }
|
||||
|
||||
public int Width { get; set; }
|
||||
|
||||
public int Height { get; set; }
|
||||
}
|
||||
|
||||
public int RefWidth { get; set; }
|
||||
|
||||
public int RefHeight { get; set; }
|
||||
|
||||
public List<CanvasZoneWrapper> Zones { get; set; }
|
||||
|
||||
public int SensitivityRadius { get; set; } = LayoutDefaultSettings.DefaultSensitivityRadius;
|
||||
}
|
||||
|
||||
public sealed class GridInfoWrapper
|
||||
{
|
||||
public int Rows { get; set; }
|
||||
|
||||
public int Columns { get; set; }
|
||||
|
||||
public List<int> RowsPercentage { get; set; }
|
||||
|
||||
public List<int> ColumnsPercentage { get; set; }
|
||||
|
||||
public int[][] CellChildMap { get; set; }
|
||||
|
||||
public bool ShowSpacing { get; set; } = LayoutDefaultSettings.DefaultShowSpacing;
|
||||
|
||||
public int Spacing { get; set; } = LayoutDefaultSettings.DefaultSpacing;
|
||||
|
||||
public int SensitivityRadius { get; set; } = LayoutDefaultSettings.DefaultSensitivityRadius;
|
||||
}
|
||||
|
||||
public struct CustomLayoutWrapper
|
||||
{
|
||||
public string Uuid { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Type { get; set; }
|
||||
|
||||
public JsonElement Info { get; set; } // CanvasInfoWrapper or GridInfoWrapper
|
||||
}
|
||||
|
||||
public struct CustomLayoutListWrapper
|
||||
{
|
||||
public List<CustomLayoutWrapper> CustomLayouts { get; set; }
|
||||
}
|
||||
|
||||
public JsonElement ToJsonElement(CanvasInfoWrapper info)
|
||||
{
|
||||
string json = JsonSerializer.Serialize(info, this.JsonOptions);
|
||||
return JsonSerializer.Deserialize<JsonElement>(json);
|
||||
}
|
||||
|
||||
public JsonElement ToJsonElement(GridInfoWrapper info)
|
||||
{
|
||||
string json = JsonSerializer.Serialize(info, this.JsonOptions);
|
||||
return JsonSerializer.Deserialize<JsonElement>(json);
|
||||
}
|
||||
|
||||
public CanvasInfoWrapper CanvasFromJsonElement(string json)
|
||||
{
|
||||
return JsonSerializer.Deserialize<CanvasInfoWrapper>(json, this.JsonOptions);
|
||||
}
|
||||
|
||||
public GridInfoWrapper GridFromJsonElement(string json)
|
||||
{
|
||||
return JsonSerializer.Deserialize<GridInfoWrapper>(json, this.JsonOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using static FancyZonesEditorCommon.Data.DefaultLayouts;
|
||||
|
||||
namespace FancyZonesEditorCommon.Data
|
||||
{
|
||||
public class DefaultLayouts : EditorData<DefaultLayoutsListWrapper>
|
||||
{
|
||||
public string File
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetDataFolder() + "\\Microsoft\\PowerToys\\FancyZones\\default-layouts.json";
|
||||
}
|
||||
}
|
||||
|
||||
public struct DefaultLayoutWrapper
|
||||
{
|
||||
public struct LayoutWrapper
|
||||
{
|
||||
public string Uuid { get; set; }
|
||||
|
||||
public string Type { get; set; }
|
||||
|
||||
public bool ShowSpacing { get; set; }
|
||||
|
||||
public int Spacing { get; set; }
|
||||
|
||||
public int ZoneCount { get; set; }
|
||||
|
||||
public int SensitivityRadius { get; set; }
|
||||
}
|
||||
|
||||
public string MonitorConfiguration { get; set; }
|
||||
|
||||
public LayoutWrapper Layout { get; set; }
|
||||
}
|
||||
|
||||
public struct DefaultLayoutsListWrapper
|
||||
{
|
||||
public List<DefaultLayoutWrapper> DefaultLayouts { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// 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.Text.Json;
|
||||
using FancyZonesEditorCommon.Utils;
|
||||
|
||||
namespace FancyZonesEditorCommon.Data
|
||||
{
|
||||
public class EditorData<T>
|
||||
{
|
||||
public string GetDataFolder()
|
||||
{
|
||||
return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
||||
}
|
||||
|
||||
protected JsonSerializerOptions JsonOptions
|
||||
{
|
||||
get
|
||||
{
|
||||
return new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = new DashCaseNamingPolicy(),
|
||||
WriteIndented = true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public T Read(string file)
|
||||
{
|
||||
IOUtils ioUtils = new IOUtils();
|
||||
string data = ioUtils.ReadFile(file);
|
||||
return JsonSerializer.Deserialize<T>(data, JsonOptions);
|
||||
}
|
||||
|
||||
public string Serialize(T data)
|
||||
{
|
||||
return JsonSerializer.Serialize(data, JsonOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
namespace FancyZonesEditorCommon.Data
|
||||
{
|
||||
public class EditorParameters : EditorData<EditorParameters.ParamsWrapper>
|
||||
{
|
||||
public string File
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetDataFolder() + "\\Microsoft\\PowerToys\\FancyZones\\editor-parameters.json";
|
||||
}
|
||||
}
|
||||
|
||||
public struct NativeMonitorDataWrapper
|
||||
{
|
||||
public string Monitor { get; set; }
|
||||
|
||||
public string MonitorInstanceId { get; set; }
|
||||
|
||||
public string MonitorSerialNumber { get; set; }
|
||||
|
||||
public int MonitorNumber { get; set; }
|
||||
|
||||
public string VirtualDesktop { get; set; }
|
||||
|
||||
public int Dpi { get; set; }
|
||||
|
||||
public int LeftCoordinate { get; set; }
|
||||
|
||||
public int TopCoordinate { get; set; }
|
||||
|
||||
public int WorkAreaWidth { get; set; }
|
||||
|
||||
public int WorkAreaHeight { get; set; }
|
||||
|
||||
public int MonitorWidth { get; set; }
|
||||
|
||||
public int MonitorHeight { get; set; }
|
||||
|
||||
public bool IsSelected { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
// using CultureInfo.InvariantCulture since this is internal data
|
||||
sb.Append("Monitor: ");
|
||||
sb.AppendLine(Monitor);
|
||||
sb.Append("Virtual desktop: ");
|
||||
sb.AppendLine(VirtualDesktop);
|
||||
sb.Append("DPI: ");
|
||||
sb.AppendLine(Dpi.ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
sb.Append("X: ");
|
||||
sb.AppendLine(LeftCoordinate.ToString(CultureInfo.InvariantCulture));
|
||||
sb.Append("Y: ");
|
||||
sb.AppendLine(TopCoordinate.ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
sb.Append("Width: ");
|
||||
sb.AppendLine(MonitorWidth.ToString(CultureInfo.InvariantCulture));
|
||||
sb.Append("Height: ");
|
||||
sb.AppendLine(MonitorHeight.ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public struct ParamsWrapper
|
||||
{
|
||||
public int ProcessId { get; set; }
|
||||
|
||||
public bool SpanZonesAcrossMonitors { get; set; }
|
||||
|
||||
public List<NativeMonitorDataWrapper> Monitors { get; set; }
|
||||
}
|
||||
|
||||
public EditorParameters()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// 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 FancyZonesEditorCommon.Data
|
||||
{
|
||||
public class LayoutDefaultSettings
|
||||
{
|
||||
// TODO: share the constants b/w C# Editor and FancyZoneLib
|
||||
public const bool DefaultShowSpacing = true;
|
||||
|
||||
public const int DefaultSpacing = 16;
|
||||
|
||||
public const int DefaultZoneCount = 3;
|
||||
|
||||
public const int DefaultSensitivityRadius = 20;
|
||||
|
||||
public const int MaxZones = 128;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using static FancyZonesEditorCommon.Data.LayoutHotkeys;
|
||||
|
||||
namespace FancyZonesEditorCommon.Data
|
||||
{
|
||||
public class LayoutHotkeys : EditorData<LayoutHotkeysWrapper>
|
||||
{
|
||||
public string File
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetDataFolder() + "\\Microsoft\\PowerToys\\FancyZones\\layout-hotkeys.json";
|
||||
}
|
||||
}
|
||||
|
||||
public struct LayoutHotkeyWrapper
|
||||
{
|
||||
public int Key { get; set; }
|
||||
|
||||
public string LayoutId { get; set; }
|
||||
}
|
||||
|
||||
public struct LayoutHotkeysWrapper
|
||||
{
|
||||
public List<LayoutHotkeyWrapper> LayoutHotkeys { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using static FancyZonesEditorCommon.Data.LayoutTemplates;
|
||||
|
||||
namespace FancyZonesEditorCommon.Data
|
||||
{
|
||||
public class LayoutTemplates : EditorData<TemplateLayoutsListWrapper>
|
||||
{
|
||||
public string File
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetDataFolder() + "\\Microsoft\\PowerToys\\FancyZones\\layout-templates.json";
|
||||
}
|
||||
}
|
||||
|
||||
public struct TemplateLayoutWrapper
|
||||
{
|
||||
public string Type { get; set; }
|
||||
|
||||
public bool ShowSpacing { get; set; }
|
||||
|
||||
public int Spacing { get; set; }
|
||||
|
||||
public int ZoneCount { get; set; }
|
||||
|
||||
public int SensitivityRadius { get; set; }
|
||||
}
|
||||
|
||||
public struct TemplateLayoutsListWrapper
|
||||
{
|
||||
public List<TemplateLayoutWrapper> LayoutTemplates { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\Version.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0-windows10.0.20348.0</TargetFramework>
|
||||
<RuntimeIdentifiers>win-x64;win-arm64</RuntimeIdentifiers>
|
||||
<Version>$(Version).0</Version>
|
||||
<Authors>Microsoft Corporation</Authors>
|
||||
<Product>PowerToys</Product>
|
||||
<Description>PowerToys FancyZonesEditorCommon</Description>
|
||||
<AssemblyName>PowerToys.FancyZonesEditorCommon</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.IO.Abstractions" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)\FancyZonesEditorCommon\</OutputPath>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -4,9 +4,7 @@
|
||||
|
||||
using System.Text.Json;
|
||||
|
||||
using FancyZonesEditor.Utils;
|
||||
|
||||
namespace FancyZonesEditor
|
||||
namespace FancyZonesEditorCommon.Utils
|
||||
{
|
||||
public class DashCaseNamingPolicy : JsonNamingPolicy
|
||||
{
|
||||
@@ -0,0 +1,54 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Abstractions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FancyZonesEditorCommon.Utils
|
||||
{
|
||||
public class IOUtils
|
||||
{
|
||||
private readonly IFileSystem _fileSystem = new FileSystem();
|
||||
|
||||
public IOUtils()
|
||||
{
|
||||
}
|
||||
|
||||
public void WriteFile(string fileName, string data)
|
||||
{
|
||||
_fileSystem.File.WriteAllText(fileName, data);
|
||||
}
|
||||
|
||||
public string ReadFile(string fileName)
|
||||
{
|
||||
if (_fileSystem.File.Exists(fileName))
|
||||
{
|
||||
var attempts = 0;
|
||||
while (attempts < 10)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (Stream inputStream = _fileSystem.File.Open(fileName, FileMode.Open))
|
||||
using (StreamReader reader = new StreamReader(inputStream))
|
||||
{
|
||||
string data = reader.ReadToEnd();
|
||||
inputStream.Close();
|
||||
return data;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Task.Delay(10).Wait();
|
||||
}
|
||||
|
||||
attempts++;
|
||||
}
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
using System.Linq;
|
||||
|
||||
namespace FancyZonesEditor.Utils
|
||||
namespace FancyZonesEditorCommon.Utils
|
||||
{
|
||||
public static class StringUtils
|
||||
{
|
||||
@@ -8,8 +8,6 @@ DraggingState::DraggingState(const std::function<void()>& keyUpdateCallback) :
|
||||
m_secondaryMouseState(false),
|
||||
m_middleMouseState(false),
|
||||
m_mouseHook(std::bind(&DraggingState::OnSecondaryMouseDown, this), std::bind(&DraggingState::OnMiddleMouseDown, this)),
|
||||
m_leftShiftKeyState(keyUpdateCallback),
|
||||
m_rightShiftKeyState(keyUpdateCallback),
|
||||
m_ctrlKeyState(keyUpdateCallback),
|
||||
m_keyUpdateCallback(keyUpdateCallback)
|
||||
{
|
||||
@@ -22,49 +20,30 @@ void DraggingState::Enable()
|
||||
m_mouseHook.enable();
|
||||
}
|
||||
|
||||
m_leftShiftKeyState.enable();
|
||||
m_rightShiftKeyState.enable();
|
||||
m_ctrlKeyState.enable();
|
||||
}
|
||||
|
||||
void DraggingState::Disable()
|
||||
{
|
||||
const bool leftShiftPressed = m_leftShiftKeyState.state();
|
||||
const bool rightShiftPressed = m_rightShiftKeyState.state();
|
||||
|
||||
if (FancyZonesSettings::settings().shiftDrag)
|
||||
{
|
||||
if (leftShiftPressed)
|
||||
{
|
||||
FancyZonesUtils::SwallowKey(VK_LSHIFT);
|
||||
}
|
||||
|
||||
if (rightShiftPressed)
|
||||
{
|
||||
FancyZonesUtils::SwallowKey(VK_RSHIFT);
|
||||
}
|
||||
}
|
||||
|
||||
m_dragging = false;
|
||||
m_secondaryMouseState = false;
|
||||
m_middleMouseState = false;
|
||||
m_shift = false;
|
||||
|
||||
m_mouseHook.disable();
|
||||
m_leftShiftKeyState.disable();
|
||||
m_rightShiftKeyState.disable();
|
||||
m_ctrlKeyState.disable();
|
||||
}
|
||||
|
||||
void DraggingState::UpdateDraggingState() noexcept
|
||||
{
|
||||
// This updates m_dragEnabled depending on if the shift key is being held down
|
||||
// This updates m_dragging depending on if the shift key is being held down
|
||||
if (FancyZonesSettings::settings().shiftDrag)
|
||||
{
|
||||
m_dragging = ((m_leftShiftKeyState.state() || m_rightShiftKeyState.state()) ^ m_secondaryMouseState);
|
||||
m_dragging = (m_shift ^ m_secondaryMouseState);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_dragging = !((m_leftShiftKeyState.state() || m_rightShiftKeyState.state()) ^ m_secondaryMouseState);
|
||||
m_dragging = !(m_shift ^ m_secondaryMouseState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,4 +75,10 @@ bool DraggingState::IsDragging() const noexcept
|
||||
bool DraggingState::IsSelectManyZonesState() const noexcept
|
||||
{
|
||||
return m_ctrlKeyState.state() || m_middleMouseState;
|
||||
}
|
||||
}
|
||||
|
||||
void DraggingState::SetShiftState(bool value) noexcept
|
||||
{
|
||||
m_shift = value;
|
||||
m_keyUpdateCallback();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ public:
|
||||
bool IsDragging() const noexcept;
|
||||
bool IsSelectManyZonesState() const noexcept;
|
||||
|
||||
void SetShiftState(bool value) noexcept;
|
||||
|
||||
private:
|
||||
void OnSecondaryMouseDown();
|
||||
void OnMiddleMouseDown();
|
||||
@@ -23,9 +25,10 @@ private:
|
||||
std::atomic<bool> m_secondaryMouseState;
|
||||
std::atomic<bool> m_middleMouseState;
|
||||
MouseButtonsHook m_mouseHook;
|
||||
KeyState<VK_LSHIFT> m_leftShiftKeyState;
|
||||
KeyState<VK_RSHIFT> m_rightShiftKeyState;
|
||||
KeyState<VK_LCONTROL, VK_RCONTROL> m_ctrlKeyState;
|
||||
|
||||
bool m_shift{};
|
||||
|
||||
std::function<void()> m_keyUpdateCallback;
|
||||
|
||||
bool m_dragging{}; // True if we should be showing zone hints while dragging
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <FancyZonesLib/FancyZonesWindowProcessing.h>
|
||||
#include <FancyZonesLib/FancyZonesWindowProperties.h>
|
||||
#include <FancyZonesLib/FancyZonesWinHookEventIDs.h>
|
||||
#include <FancyZonesLib/KeyboardInput.h>
|
||||
#include <FancyZonesLib/MonitorUtils.h>
|
||||
#include <FancyZonesLib/on_thread_executor.h>
|
||||
#include <FancyZonesLib/Settings.h>
|
||||
@@ -143,6 +144,7 @@ public:
|
||||
void ToggleEditor() noexcept;
|
||||
|
||||
LRESULT WndProc(HWND, UINT, WPARAM, LPARAM) noexcept;
|
||||
void OnKeyboardInput(WPARAM flags, HRAWINPUT hInput) noexcept;
|
||||
void OnDisplayChange(DisplayChangeType changeType) noexcept;
|
||||
bool AddWorkArea(HMONITOR monitor, const FancyZonesDataTypes::WorkAreaId& id, const FancyZonesUtils::Rect& rect) noexcept;
|
||||
|
||||
@@ -220,7 +222,13 @@ FancyZones::Run() noexcept
|
||||
m_window = CreateWindowExW(WS_EX_TOOLWINDOW, NonLocalizable::ToolWindowClassName, L"", WS_POPUP, 0, 0, 0, 0, nullptr, nullptr, m_hinstance, this);
|
||||
if (!m_window)
|
||||
{
|
||||
Logger::error(L"Failed to create FancyZones window");
|
||||
Logger::critical(L"Failed to create FancyZones window");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!KeyboardInput::Initialize(m_window))
|
||||
{
|
||||
Logger::critical(L"Failed to register raw input device");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -580,6 +588,12 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_INPUT:
|
||||
{
|
||||
OnKeyboardInput(wparam, reinterpret_cast<HRAWINPUT>(lparam));
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_SETTINGCHANGE:
|
||||
{
|
||||
if (wparam == SPI_SETWORKAREA)
|
||||
@@ -717,6 +731,26 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FancyZones::OnKeyboardInput(WPARAM /*flags*/, HRAWINPUT hInput) noexcept
|
||||
{
|
||||
auto input = KeyboardInput::OnKeyboardInput(hInput);
|
||||
if (!input.has_value())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (input.value().vkKey)
|
||||
{
|
||||
case VK_SHIFT:
|
||||
{
|
||||
m_draggingState.SetShiftState(input.value().pressed);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void FancyZones::OnDisplayChange(DisplayChangeType changeType) noexcept
|
||||
{
|
||||
Logger::info(L"Display changed, type: {}", DisplayChangeTypeName(changeType));
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
<ClInclude Include="FancyZonesData.h" />
|
||||
<ClInclude Include="GuidUtils.h" />
|
||||
<ClInclude Include="JsonHelpers.h" />
|
||||
<ClInclude Include="KeyboardInput.h" />
|
||||
<ClInclude Include="KeyState.h" />
|
||||
<ClInclude Include="FancyZonesData\LayoutHotkeys.h" />
|
||||
<ClInclude Include="Layout.h" />
|
||||
@@ -114,6 +115,7 @@
|
||||
<ClCompile Include="FancyZonesData\LayoutHotkeys.cpp">
|
||||
<PrecompiledHeaderFile>../pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<ClCompile Include="KeyboardInput.cpp" />
|
||||
<ClCompile Include="Layout.cpp" />
|
||||
<ClCompile Include="LayoutConfigurator.cpp" />
|
||||
<ClCompile Include="LayoutAssignedWindows.cpp" />
|
||||
|
||||
@@ -168,6 +168,9 @@
|
||||
<ClInclude Include="FancyZonesData\LastUsedVirtualDesktop.h">
|
||||
<Filter>Header Files\FancyZonesData</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="KeyboardInput.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
@@ -278,6 +281,9 @@
|
||||
<ClCompile Include="FancyZonesWindowProcessing.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="KeyboardInput.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
||||
43
src/modules/fancyzones/FancyZonesLib/KeyboardInput.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
#include "pch.h"
|
||||
#include "KeyboardInput.h"
|
||||
|
||||
#include <hidUsage.h>
|
||||
|
||||
#include <common/logger/logger.h>
|
||||
#include <common/utils/winapi_error.h>
|
||||
|
||||
bool KeyboardInput::Initialize(HWND window)
|
||||
{
|
||||
RAWINPUTDEVICE inputDevice{};
|
||||
inputDevice.usUsagePage = HID_USAGE_PAGE_GENERIC;
|
||||
inputDevice.usUsage = HID_USAGE_GENERIC_KEYBOARD;
|
||||
inputDevice.dwFlags = RIDEV_INPUTSINK;
|
||||
inputDevice.hwndTarget = window;
|
||||
|
||||
bool res = RegisterRawInputDevices(&inputDevice, 1, sizeof(inputDevice));
|
||||
if (!res)
|
||||
{
|
||||
Logger::error(L"RegisterRawInputDevices error: {}", get_last_error_or_default(GetLastError()));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::optional<KeyboardInput::Key> KeyboardInput::OnKeyboardInput(HRAWINPUT hInput)
|
||||
{
|
||||
RAWINPUT input;
|
||||
UINT size = sizeof(input);
|
||||
auto result = GetRawInputData(hInput, RID_INPUT, &input, &size, sizeof(RAWINPUTHEADER));
|
||||
if (result < sizeof(RAWINPUTHEADER))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (input.header.dwType == RIM_TYPEKEYBOARD)
|
||||
{
|
||||
bool pressed = (input.data.keyboard.Flags & RI_KEY_BREAK) == 0;
|
||||
return KeyboardInput::Key{ input.data.keyboard.VKey, pressed };
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
17
src/modules/fancyzones/FancyZonesLib/KeyboardInput.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
class KeyboardInput
|
||||
{
|
||||
public:
|
||||
struct Key
|
||||
{
|
||||
USHORT vkKey{};
|
||||
bool pressed{};
|
||||
};
|
||||
|
||||
KeyboardInput() = default;
|
||||
~KeyboardInput() = default;
|
||||
|
||||
static bool Initialize(HWND window);
|
||||
static std::optional<Key> OnKeyboardInput(HRAWINPUT hInput);
|
||||
};
|
||||
35
src/modules/fancyzones/UITests-FancyZones/Init.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace Microsoft.FancyZonesEditor.UITests
|
||||
{
|
||||
[TestClass]
|
||||
public class Init
|
||||
{
|
||||
private static Process? appDriver;
|
||||
|
||||
[AssemblyInitialize]
|
||||
public static void SetupAll(TestContext context)
|
||||
{
|
||||
string winAppDriverPath = "C:\\Program Files (x86)\\Windows Application Driver\\WinAppDriver.exe";
|
||||
context.WriteLine($"Attempting to launch WinAppDriver at: {winAppDriverPath}");
|
||||
appDriver = Process.Start(winAppDriverPath);
|
||||
}
|
||||
|
||||
[AssemblyCleanup]
|
||||
public static void CleanupAll()
|
||||
{
|
||||
try
|
||||
{
|
||||
appDriver?.Kill();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// 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.FancyZones.UnitTests.Utils;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace UITests_FancyZones
|
||||
{
|
||||
[TestClass]
|
||||
public class RunFancyZonesTest
|
||||
{
|
||||
private static FancyZonesSession? _session;
|
||||
|
||||
[ClassInitialize]
|
||||
public static void ClassInitialize(TestContext testContext)
|
||||
{
|
||||
_session = new FancyZonesSession(testContext);
|
||||
}
|
||||
|
||||
[ClassCleanup]
|
||||
public static void ClassCleanup()
|
||||
{
|
||||
_session?.Close();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RunFancyZones()
|
||||
{
|
||||
Assert.IsNotNull(_session?.FancyZonesProcess);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\Version.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0-windows10.0.20348.0</TargetFramework>
|
||||
<TargetPlatformMinVersion>10.0.19041.0</TargetPlatformMinVersion>
|
||||
<SupportedOSPlatformVersion>10.0.19041.0</SupportedOSPlatformVersion>
|
||||
<ProjectGuid>{FE38FC07-1C05-4B57-ADA3-2FE2F53C6A52}</ProjectGuid>
|
||||
<RootNamespace>Microsoft.FancyZones.UITests</RootNamespace>
|
||||
<IsPackable>false</IsPackable>
|
||||
<Nullable>enable</Nullable>
|
||||
<OutputType>Library</OutputType>
|
||||
<Version>$(Version).0</Version>
|
||||
<!-- This is a UI test, so don't run as part of MSBuild -->
|
||||
<RunVSTest>false</RunVSTest>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)\tests\UITests-FancyZones\</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Appium.WebDriver" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="MSTest.TestAdapter" />
|
||||
<PackageReference Include="MSTest.TestFramework" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Properties\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace Microsoft.FancyZones.UnitTests.Utils
|
||||
{
|
||||
public class FancyZonesSession
|
||||
{
|
||||
private const string FancyZonesPath = @"\..\..\..\PowerToys.FancyZones.exe";
|
||||
private const string FancyZonesProcessName = "PowerToys.FancyZones";
|
||||
|
||||
private bool stopFancyZones = true;
|
||||
|
||||
public Process? FancyZonesProcess { get; }
|
||||
|
||||
public FancyZonesSession(TestContext testContext)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Check if FancyZones is already running
|
||||
Process[] runningFZ = Process.GetProcessesByName(FancyZonesProcessName);
|
||||
if (runningFZ.Length > 0)
|
||||
{
|
||||
FancyZonesProcess = runningFZ[0];
|
||||
stopFancyZones = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Launch FancyZones
|
||||
string? path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
path += FancyZonesPath;
|
||||
|
||||
ProcessStartInfo info = new ProcessStartInfo(path);
|
||||
FancyZonesProcess = Process.Start(info);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
testContext.WriteLine(ex.Message);
|
||||
}
|
||||
|
||||
Assert.IsNotNull(FancyZonesProcess, "FancyZones process not started");
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
// Close the application
|
||||
if (FancyZonesProcess != null)
|
||||
{
|
||||
if (stopFancyZones)
|
||||
{
|
||||
FancyZonesProcess.Kill();
|
||||
}
|
||||
|
||||
FancyZonesProcess.Close();
|
||||
FancyZonesProcess.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
35
src/modules/fancyzones/UITests-FancyZonesEditor/Init.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace Microsoft.FancyZonesEditor.UITests
|
||||
{
|
||||
[TestClass]
|
||||
public class Init
|
||||
{
|
||||
private static Process? appDriver;
|
||||
|
||||
[AssemblyInitialize]
|
||||
public static void SetupAll(TestContext context)
|
||||
{
|
||||
string winAppDriverPath = "C:\\Program Files (x86)\\Windows Application Driver\\WinAppDriver.exe";
|
||||
context.WriteLine($"Attempting to launch WinAppDriver at: {winAppDriverPath}");
|
||||
appDriver = Process.Start(winAppDriverPath);
|
||||
}
|
||||
|
||||
[AssemblyCleanup]
|
||||
public static void CleanupAll()
|
||||
{
|
||||
try
|
||||
{
|
||||
appDriver?.Kill();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using FancyZonesEditorCommon.Data;
|
||||
using Microsoft.FancyZonesEditor.UITests;
|
||||
using Microsoft.FancyZonesEditor.UnitTests.Utils;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace UITests_FancyZonesEditor
|
||||
{
|
||||
[TestClass]
|
||||
public class RunFancyZonesEditorTest
|
||||
{
|
||||
private static FancyZonesEditorSession? _session;
|
||||
private static TestContext? _context;
|
||||
|
||||
[ClassInitialize]
|
||||
public static void ClassInitialize(TestContext testContext)
|
||||
{
|
||||
_context = testContext;
|
||||
|
||||
// prepare files to launch Editor without errors
|
||||
EditorParameters editorParameters = new EditorParameters();
|
||||
EditorParameters.ParamsWrapper parameters = new EditorParameters.ParamsWrapper
|
||||
{
|
||||
ProcessId = 1,
|
||||
SpanZonesAcrossMonitors = false,
|
||||
Monitors = new List<EditorParameters.NativeMonitorDataWrapper>
|
||||
{
|
||||
new EditorParameters.NativeMonitorDataWrapper
|
||||
{
|
||||
Monitor = "monitor-1",
|
||||
MonitorInstanceId = "instance-id-1",
|
||||
MonitorSerialNumber = "serial-number-1",
|
||||
MonitorNumber = 1,
|
||||
VirtualDesktop = "{FF34D993-73F3-4B8C-AA03-73730A01D6A8}",
|
||||
Dpi = 96,
|
||||
LeftCoordinate = 0,
|
||||
TopCoordinate = 0,
|
||||
WorkAreaHeight = 1040,
|
||||
WorkAreaWidth = 1920,
|
||||
MonitorHeight = 1080,
|
||||
MonitorWidth = 1920,
|
||||
IsSelected = true,
|
||||
},
|
||||
},
|
||||
};
|
||||
FancyZonesEditorSession.Files.ParamsIOHelper.WriteData(editorParameters.Serialize(parameters));
|
||||
|
||||
LayoutTemplates layoutTemplates = new LayoutTemplates();
|
||||
LayoutTemplates.TemplateLayoutsListWrapper templateLayoutsListWrapper = new LayoutTemplates.TemplateLayoutsListWrapper
|
||||
{
|
||||
LayoutTemplates = new List<LayoutTemplates.TemplateLayoutWrapper>
|
||||
{
|
||||
new LayoutTemplates.TemplateLayoutWrapper
|
||||
{
|
||||
Type = Constants.TemplateLayoutJsonTags[Constants.TemplateLayout.Empty],
|
||||
},
|
||||
new LayoutTemplates.TemplateLayoutWrapper
|
||||
{
|
||||
Type = Constants.TemplateLayoutJsonTags[Constants.TemplateLayout.Focus],
|
||||
ZoneCount = 10,
|
||||
},
|
||||
new LayoutTemplates.TemplateLayoutWrapper
|
||||
{
|
||||
Type = Constants.TemplateLayoutJsonTags[Constants.TemplateLayout.Rows],
|
||||
ZoneCount = 2,
|
||||
ShowSpacing = true,
|
||||
Spacing = 10,
|
||||
SensitivityRadius = 10,
|
||||
},
|
||||
new LayoutTemplates.TemplateLayoutWrapper
|
||||
{
|
||||
Type = Constants.TemplateLayoutJsonTags[Constants.TemplateLayout.Columns],
|
||||
ZoneCount = 2,
|
||||
ShowSpacing = true,
|
||||
Spacing = 20,
|
||||
SensitivityRadius = 20,
|
||||
},
|
||||
new LayoutTemplates.TemplateLayoutWrapper
|
||||
{
|
||||
Type = Constants.TemplateLayoutJsonTags[Constants.TemplateLayout.Grid],
|
||||
ZoneCount = 4,
|
||||
ShowSpacing = false,
|
||||
Spacing = 10,
|
||||
SensitivityRadius = 30,
|
||||
},
|
||||
new LayoutTemplates.TemplateLayoutWrapper
|
||||
{
|
||||
Type = Constants.TemplateLayoutJsonTags[Constants.TemplateLayout.PriorityGrid],
|
||||
ZoneCount = 3,
|
||||
ShowSpacing = true,
|
||||
Spacing = 1,
|
||||
SensitivityRadius = 40,
|
||||
},
|
||||
},
|
||||
};
|
||||
FancyZonesEditorSession.Files.LayoutTemplatesIOHelper.WriteData(layoutTemplates.Serialize(templateLayoutsListWrapper));
|
||||
|
||||
CustomLayouts customLayouts = new CustomLayouts();
|
||||
CustomLayouts.CustomLayoutListWrapper customLayoutListWrapper = new CustomLayouts.CustomLayoutListWrapper
|
||||
{
|
||||
CustomLayouts = new List<CustomLayouts.CustomLayoutWrapper> { },
|
||||
};
|
||||
FancyZonesEditorSession.Files.CustomLayoutsIOHelper.WriteData(customLayouts.Serialize(customLayoutListWrapper));
|
||||
|
||||
DefaultLayouts defaultLayouts = new DefaultLayouts();
|
||||
DefaultLayouts.DefaultLayoutsListWrapper defaultLayoutsListWrapper = new DefaultLayouts.DefaultLayoutsListWrapper
|
||||
{
|
||||
DefaultLayouts = new List<DefaultLayouts.DefaultLayoutWrapper> { },
|
||||
};
|
||||
FancyZonesEditorSession.Files.DefaultLayoutsIOHelper.WriteData(defaultLayouts.Serialize(defaultLayoutsListWrapper));
|
||||
|
||||
LayoutHotkeys layoutHotkeys = new LayoutHotkeys();
|
||||
LayoutHotkeys.LayoutHotkeysWrapper layoutHotkeysWrapper = new LayoutHotkeys.LayoutHotkeysWrapper
|
||||
{
|
||||
LayoutHotkeys = new List<LayoutHotkeys.LayoutHotkeyWrapper> { },
|
||||
};
|
||||
FancyZonesEditorSession.Files.LayoutHotkeysIOHelper.WriteData(layoutHotkeys.Serialize(layoutHotkeysWrapper));
|
||||
|
||||
AppliedLayouts appliedLayouts = new AppliedLayouts();
|
||||
AppliedLayouts.AppliedLayoutsListWrapper appliedLayoutsWrapper = new AppliedLayouts.AppliedLayoutsListWrapper
|
||||
{
|
||||
AppliedLayouts = new List<AppliedLayouts.AppliedLayoutWrapper> { },
|
||||
};
|
||||
FancyZonesEditorSession.Files.AppliedLayoutsIOHelper.WriteData(appliedLayouts.Serialize(appliedLayoutsWrapper));
|
||||
}
|
||||
|
||||
[ClassCleanup]
|
||||
public static void ClassCleanup()
|
||||
{
|
||||
FancyZonesEditorSession.Files.Restore();
|
||||
_context = null;
|
||||
}
|
||||
|
||||
[TestInitialize]
|
||||
public void TestInitialize()
|
||||
{
|
||||
_session = new FancyZonesEditorSession(_context!);
|
||||
}
|
||||
|
||||
[TestCleanup]
|
||||
public void TestCleanup()
|
||||
{
|
||||
_session?.Close(_context!);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void OpenEditorWindow() // verify the session is initialized
|
||||
{
|
||||
Assert.IsNotNull(_session?.Session);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void OpenNewLayoutDialog() // verify the new layout dialog is opened
|
||||
{
|
||||
_session?.Click_CreateNewLayout();
|
||||
Assert.IsNotNull(_session?.Session?.FindElementsByName("Choose layout type")); // check the pane header
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void OpenEditLayoutDialog() // verify the edit layout dialog is opened
|
||||
{
|
||||
_session?.Click_EditLayout(TestConstants.TemplateLayoutNames[Constants.TemplateLayout.Grid]);
|
||||
Assert.IsNotNull(_session?.Session?.FindElementByAccessibilityId("EditLayoutDialogTitle")); // check the pane header
|
||||
Assert.IsNotNull(_session?.Session?.FindElementsByName("Edit 'Grid'")); // verify it's opened for the correct layout
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void OpenContextMenu() // verify the context menu is opened
|
||||
{
|
||||
Assert.IsNotNull(_session?.OpenContextMenu(TestConstants.TemplateLayoutNames[Constants.TemplateLayout.Columns]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using static FancyZonesEditorCommon.Data.Constants;
|
||||
|
||||
namespace Microsoft.FancyZonesEditor.UITests
|
||||
{
|
||||
public static class TestConstants
|
||||
{
|
||||
public static readonly Dictionary<TemplateLayout, string> TemplateLayoutNames = new Dictionary<TemplateLayout, string>()
|
||||
{
|
||||
{ TemplateLayout.Empty, "No layout" },
|
||||
{ TemplateLayout.Focus, "Focus" },
|
||||
{ TemplateLayout.Rows, "Rows" },
|
||||
{ TemplateLayout.Columns, "Columns" },
|
||||
{ TemplateLayout.Grid, "Grid" },
|
||||
{ TemplateLayout.PriorityGrid, "PriorityGrid" },
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\Version.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0-windows10.0.20348.0</TargetFramework>
|
||||
<TargetPlatformMinVersion>10.0.19041.0</TargetPlatformMinVersion>
|
||||
<SupportedOSPlatformVersion>10.0.19041.0</SupportedOSPlatformVersion>
|
||||
<ProjectGuid>{3A9A791E-94A9-49F8-8401-C11CE288D5FB}</ProjectGuid>
|
||||
<RootNamespace>Microsoft.FancyZonesEditor.UITests</RootNamespace>
|
||||
<IsPackable>false</IsPackable>
|
||||
<Nullable>enable</Nullable>
|
||||
<OutputType>Library</OutputType>
|
||||
<Version>$(Version).0</Version>
|
||||
<!-- This is a UI test, so don't run as part of MSBuild -->
|
||||
<RunVSTest>false</RunVSTest>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)\tests\UITests-FancyZonesEditor\</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Appium.WebDriver" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="MSTest.TestAdapter" />
|
||||
<PackageReference Include="MSTest.TestFramework" />
|
||||
<PackageReference Include="System.IO.Abstractions" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Properties\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\FancyZonesEditorCommon\FancyZonesEditorCommon.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using FancyZonesEditorCommon.Data;
|
||||
|
||||
namespace Microsoft.FancyZonesEditor.UITests.Utils
|
||||
{
|
||||
public class FancyZonesEditorFiles
|
||||
{
|
||||
public IOTestHelper ParamsIOHelper { get; }
|
||||
|
||||
public IOTestHelper AppliedLayoutsIOHelper { get; }
|
||||
|
||||
public IOTestHelper CustomLayoutsIOHelper { get; }
|
||||
|
||||
public IOTestHelper DefaultLayoutsIOHelper { get; }
|
||||
|
||||
public IOTestHelper LayoutHotkeysIOHelper { get; }
|
||||
|
||||
public IOTestHelper LayoutTemplatesIOHelper { get; }
|
||||
|
||||
public FancyZonesEditorFiles()
|
||||
{
|
||||
ParamsIOHelper = new IOTestHelper(new EditorParameters().File);
|
||||
AppliedLayoutsIOHelper = new IOTestHelper(new AppliedLayouts().File);
|
||||
CustomLayoutsIOHelper = new IOTestHelper(new CustomLayouts().File);
|
||||
DefaultLayoutsIOHelper = new IOTestHelper(new DefaultLayouts().File);
|
||||
LayoutHotkeysIOHelper = new IOTestHelper(new LayoutHotkeys().File);
|
||||
LayoutTemplatesIOHelper = new IOTestHelper(new LayoutTemplates().File);
|
||||
}
|
||||
|
||||
public void Restore()
|
||||
{
|
||||
ParamsIOHelper.RestoreData();
|
||||
AppliedLayoutsIOHelper.RestoreData();
|
||||
CustomLayoutsIOHelper.RestoreData();
|
||||
DefaultLayoutsIOHelper.RestoreData();
|
||||
LayoutHotkeysIOHelper.RestoreData();
|
||||
LayoutTemplatesIOHelper.RestoreData();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Microsoft.FancyZonesEditor.UITests.Utils;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using OpenQA.Selenium;
|
||||
using OpenQA.Selenium.Appium;
|
||||
using OpenQA.Selenium.Appium.Windows;
|
||||
using OpenQA.Selenium.Interactions;
|
||||
|
||||
namespace Microsoft.FancyZonesEditor.UnitTests.Utils
|
||||
{
|
||||
public class FancyZonesEditorSession
|
||||
{
|
||||
protected const string WindowsApplicationDriverUrl = "http://127.0.0.1:4723";
|
||||
private const string FancyZonesEditorPath = @"\..\..\..\PowerToys.FancyZonesEditor.exe";
|
||||
|
||||
private static FancyZonesEditorFiles? _files;
|
||||
|
||||
public static FancyZonesEditorFiles Files
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_files == null)
|
||||
{
|
||||
_files = new FancyZonesEditorFiles();
|
||||
}
|
||||
|
||||
return _files;
|
||||
}
|
||||
}
|
||||
|
||||
public WindowsDriver<WindowsElement>? Session { get; }
|
||||
|
||||
public WindowsElement? MainEditorWindow { get; }
|
||||
|
||||
public FancyZonesEditorSession(TestContext testContext)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Launch FancyZonesEditor
|
||||
string? path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
path += FancyZonesEditorPath;
|
||||
|
||||
AppiumOptions opts = new AppiumOptions();
|
||||
opts.AddAdditionalCapability("app", path);
|
||||
Session = new WindowsDriver<WindowsElement>(new Uri(WindowsApplicationDriverUrl), opts);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
testContext.WriteLine(ex.Message);
|
||||
}
|
||||
|
||||
Assert.IsNotNull(Session, "Session not initialized");
|
||||
|
||||
testContext.WriteLine("Session: " + Session.SessionId.ToString());
|
||||
testContext.WriteLine("Title: " + Session.Title);
|
||||
|
||||
// Set implicit timeout to make element search to retry every 500 ms
|
||||
Session.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(3);
|
||||
|
||||
// Find main editor window
|
||||
try
|
||||
{
|
||||
MainEditorWindow = Session.FindElementByAccessibilityId("MainWindow1");
|
||||
}
|
||||
catch
|
||||
{
|
||||
Assert.IsNotNull(MainEditorWindow, "Main editor window not found");
|
||||
}
|
||||
}
|
||||
|
||||
public void Close(TestContext testContext)
|
||||
{
|
||||
// Close the session
|
||||
if (Session != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// FZEditor application can be closed by explicitly closing main editor window
|
||||
MainEditorWindow?.SendKeys(Keys.Alt + Keys.F4);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
testContext.WriteLine(ex.Message);
|
||||
}
|
||||
|
||||
Session.Quit();
|
||||
Session.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private WindowsElement? GetLayout(string layoutName)
|
||||
{
|
||||
var listItem = Session?.FindElementByName(layoutName);
|
||||
Assert.IsNotNull(listItem, "Layout " + layoutName + " not found");
|
||||
return listItem;
|
||||
}
|
||||
|
||||
public WindowsElement? OpenContextMenu(string layoutName)
|
||||
{
|
||||
RightClick_Layout(layoutName);
|
||||
var menu = Session?.FindElementByClassName("ContextMenu");
|
||||
Assert.IsNotNull(menu, "Context menu not found");
|
||||
return menu;
|
||||
}
|
||||
|
||||
public void Click_CreateNewLayout()
|
||||
{
|
||||
var button = Session?.FindElementByAccessibilityId("NewLayoutButton");
|
||||
Assert.IsNotNull(button, "Create new layout button not found");
|
||||
button?.Click();
|
||||
}
|
||||
|
||||
public void Click_EditLayout(string layoutName)
|
||||
{
|
||||
var layout = GetLayout(layoutName);
|
||||
var editButton = layout?.FindElementByAccessibilityId("EditLayoutButton");
|
||||
Assert.IsNotNull(editButton, "Edit button not found");
|
||||
editButton.Click();
|
||||
}
|
||||
|
||||
public void RightClick_Layout(string layoutName)
|
||||
{
|
||||
var layout = GetLayout(layoutName);
|
||||
Actions actions = new Actions(Session);
|
||||
actions.MoveToElement(layout);
|
||||
actions.MoveByOffset(30, 30);
|
||||
actions.ContextClick();
|
||||
actions.Build().Perform();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Abstractions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.FancyZonesEditor.UITests.Utils
|
||||
{
|
||||
public class IOTestHelper
|
||||
{
|
||||
private readonly IFileSystem _fileSystem = new FileSystem();
|
||||
|
||||
private string _file;
|
||||
|
||||
private string _data = string.Empty;
|
||||
|
||||
public IOTestHelper(string file)
|
||||
{
|
||||
_file = file;
|
||||
|
||||
if (_fileSystem.File.Exists(_file))
|
||||
{
|
||||
_data = ReadFile(_file);
|
||||
}
|
||||
else
|
||||
{
|
||||
_fileSystem.Directory.CreateDirectory(Path.GetDirectoryName(file));
|
||||
}
|
||||
}
|
||||
|
||||
~IOTestHelper()
|
||||
{
|
||||
RestoreData();
|
||||
}
|
||||
|
||||
public void RestoreData()
|
||||
{
|
||||
if (_data != string.Empty)
|
||||
{
|
||||
WriteData(_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
DeleteFile();
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteData(string data)
|
||||
{
|
||||
var attempts = 0;
|
||||
while (attempts < 10)
|
||||
{
|
||||
try
|
||||
{
|
||||
_fileSystem.File.WriteAllText(_file, data);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Task.Delay(10).Wait();
|
||||
}
|
||||
|
||||
attempts++;
|
||||
}
|
||||
}
|
||||
|
||||
private string ReadFile(string fileName)
|
||||
{
|
||||
var attempts = 0;
|
||||
while (attempts < 10)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (Stream inputStream = _fileSystem.File.Open(fileName, FileMode.Open))
|
||||
using (StreamReader reader = new StreamReader(inputStream))
|
||||
{
|
||||
string data = reader.ReadToEnd();
|
||||
inputStream.Close();
|
||||
return data;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Task.Delay(10).Wait();
|
||||
}
|
||||
|
||||
attempts++;
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public void DeleteFile()
|
||||
{
|
||||
var attempts = 0;
|
||||
while (attempts < 10)
|
||||
{
|
||||
try
|
||||
{
|
||||
_fileSystem.File.Delete(_file);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Task.Delay(10).Wait();
|
||||
}
|
||||
|
||||
attempts++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,6 +69,7 @@
|
||||
<ProjectReference Include="..\..\..\..\common\interop\PowerToys.Interop.vcxproj" />
|
||||
<ProjectReference Include="..\..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\common\Common.UI\Common.UI.csproj" />
|
||||
<ProjectReference Include="..\..\FancyZonesEditorCommon\FancyZonesEditorCommon.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FancyZonesEditorCommon.Data;
|
||||
|
||||
namespace FancyZonesEditor.Models
|
||||
{
|
||||
@@ -99,7 +100,7 @@ namespace FancyZonesEditor.Models
|
||||
}
|
||||
}
|
||||
|
||||
private bool _showSpacing = LayoutSettings.DefaultShowSpacing;
|
||||
private bool _showSpacing = LayoutDefaultSettings.DefaultShowSpacing;
|
||||
|
||||
// Spacing - free space between cells
|
||||
public int Spacing
|
||||
@@ -129,7 +130,7 @@ namespace FancyZonesEditor.Models
|
||||
get { return 1000; }
|
||||
}
|
||||
|
||||
private int _spacing = LayoutSettings.DefaultSpacing;
|
||||
private int _spacing = LayoutDefaultSettings.DefaultSpacing;
|
||||
|
||||
public GridLayoutModel()
|
||||
: base()
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Runtime.CompilerServices;
|
||||
using FancyZonesEditorCommon.Data;
|
||||
|
||||
namespace FancyZonesEditor.Models
|
||||
{
|
||||
@@ -195,7 +196,7 @@ namespace FancyZonesEditor.Models
|
||||
}
|
||||
}
|
||||
|
||||
private int _sensitivityRadius = LayoutSettings.DefaultSensitivityRadius;
|
||||
private int _sensitivityRadius = LayoutDefaultSettings.DefaultSensitivityRadius;
|
||||
|
||||
public int SensitivityRadiusMinimum
|
||||
{
|
||||
@@ -304,13 +305,13 @@ namespace FancyZonesEditor.Models
|
||||
}
|
||||
}
|
||||
|
||||
private int _zoneCount = LayoutSettings.DefaultZoneCount;
|
||||
private int _zoneCount = LayoutDefaultSettings.DefaultZoneCount;
|
||||
|
||||
public bool IsZoneAddingAllowed
|
||||
{
|
||||
get
|
||||
{
|
||||
return TemplateZoneCount < LayoutSettings.MaxZones;
|
||||
return TemplateZoneCount < LayoutDefaultSettings.MaxZones;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,32 +3,22 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using FancyZonesEditor.Models;
|
||||
using FancyZonesEditorCommon.Data;
|
||||
|
||||
namespace FancyZonesEditor
|
||||
{
|
||||
public class LayoutSettings
|
||||
{
|
||||
// TODO: share the constants b/w C# Editor and FancyZoneLib
|
||||
public const bool DefaultShowSpacing = true;
|
||||
|
||||
public const int DefaultSpacing = 16;
|
||||
|
||||
public const int DefaultZoneCount = 3;
|
||||
|
||||
public const int DefaultSensitivityRadius = 20;
|
||||
|
||||
public const int MaxZones = 128;
|
||||
|
||||
public string ZonesetUuid { get; set; } = string.Empty;
|
||||
|
||||
public LayoutType Type { get; set; } = LayoutType.PriorityGrid;
|
||||
|
||||
public bool ShowSpacing { get; set; } = DefaultShowSpacing;
|
||||
public bool ShowSpacing { get; set; } = LayoutDefaultSettings.DefaultShowSpacing;
|
||||
|
||||
public int Spacing { get; set; } = DefaultSpacing;
|
||||
public int Spacing { get; set; } = LayoutDefaultSettings.DefaultSpacing;
|
||||
|
||||
public int ZoneCount { get; set; } = DefaultZoneCount;
|
||||
public int ZoneCount { get; set; } = LayoutDefaultSettings.DefaultZoneCount;
|
||||
|
||||
public int SensitivityRadius { get; set; } = DefaultSensitivityRadius;
|
||||
public int SensitivityRadius { get; set; } = LayoutDefaultSettings.DefaultSensitivityRadius;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -487,6 +487,15 @@ namespace FancyZonesEditor.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to An error occurred while parsing editor parameters..
|
||||
/// </summary>
|
||||
public static string Error_Parsing_Editor_Parameters_Message {
|
||||
get {
|
||||
return ResourceManager.GetString("Error_Parsing_Editor_Parameters_Message", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to An error occurred while parsing layout hotkeys..
|
||||
/// </summary>
|
||||
|
||||
@@ -435,4 +435,7 @@
|
||||
<data name="Set_Layout_As_Vertical_Default" xml:space="preserve">
|
||||
<value>Set layout as a default for vertical monitor orientation</value>
|
||||
</data>
|
||||
<data name="Error_Parsing_Editor_Parameters_Message" xml:space="preserve">
|
||||
<value>An error occurred while parsing editor parameters.</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace FancyZonesEditor.Utils
|
||||
{
|
||||
public struct ParsingResult
|
||||
{
|
||||
public bool Result { get; }
|
||||
|
||||
public string Message { get; }
|
||||
|
||||
public string MalformedData { get; }
|
||||
|
||||
public ParsingResult(bool result, string message = "", string data = "")
|
||||
{
|
||||
Result = result;
|
||||
Message = message;
|
||||
MalformedData = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,8 @@ namespace
|
||||
{
|
||||
const wchar_t c_imageResizerDataFilePath[] = L"\\image-resizer-settings.json";
|
||||
const wchar_t c_rootRegPath[] = L"Software\\Microsoft\\ImageResizer";
|
||||
const wchar_t c_enabled[] = L"Enabled";
|
||||
const wchar_t c_enabled[] = L"enabled";
|
||||
const wchar_t c_ImageResizer[] = L"Image Resizer";
|
||||
|
||||
unsigned int RegReadInteger(const std::wstring& valueName, unsigned int defaultValue)
|
||||
{
|
||||
@@ -45,6 +46,7 @@ namespace
|
||||
|
||||
CSettings::CSettings()
|
||||
{
|
||||
generalJsonFilePath = PTSettingsHelper::get_powertoys_general_save_file_location();
|
||||
std::wstring oldSavePath = PTSettingsHelper::get_module_save_folder_location(ImageResizerConstants::ModuleOldSaveFolderKey);
|
||||
std::wstring savePath = PTSettingsHelper::get_module_save_folder_location(ImageResizerConstants::ModuleSaveFolderKey);
|
||||
std::error_code ec;
|
||||
@@ -62,8 +64,6 @@ void CSettings::Save()
|
||||
{
|
||||
json::JsonObject jsonData;
|
||||
|
||||
jsonData.SetNamedValue(c_enabled, json::value(settings.enabled));
|
||||
|
||||
json::to_file(jsonFilePath, jsonData);
|
||||
GetSystemTimeAsFileTime(&lastLoadedTime);
|
||||
}
|
||||
@@ -82,6 +82,32 @@ void CSettings::Load()
|
||||
}
|
||||
}
|
||||
|
||||
void CSettings::RefreshEnabledState()
|
||||
{
|
||||
// Load json settings from data file if it is modified in the meantime.
|
||||
FILETIME lastModifiedTime{};
|
||||
if (!(LastModifiedTime(generalJsonFilePath, &lastModifiedTime) &&
|
||||
CompareFileTime(&lastModifiedTime, &lastLoadedGeneralSettingsTime) == 1))
|
||||
return;
|
||||
|
||||
lastLoadedGeneralSettingsTime = lastModifiedTime;
|
||||
|
||||
auto json = json::from_file(generalJsonFilePath);
|
||||
if (!json)
|
||||
return;
|
||||
|
||||
const json::JsonObject& jsonSettings = json.value();
|
||||
try
|
||||
{
|
||||
json::JsonObject modulesEnabledState;
|
||||
json::get(jsonSettings, c_enabled, modulesEnabledState, json::JsonObject{});
|
||||
json::get(modulesEnabledState, c_ImageResizer, settings.enabled, true);
|
||||
}
|
||||
catch (const winrt::hresult_error&)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
void CSettings::Reload()
|
||||
{
|
||||
// Load json settings from data file if it is modified in the meantime.
|
||||
@@ -106,10 +132,7 @@ void CSettings::ParseJson()
|
||||
const json::JsonObject& jsonSettings = json.value();
|
||||
try
|
||||
{
|
||||
if (json::has(jsonSettings, c_enabled, json::JsonValueType::Boolean))
|
||||
{
|
||||
settings.enabled = jsonSettings.GetNamedBoolean(c_enabled);
|
||||
}
|
||||
// NB: add any new settings here
|
||||
}
|
||||
catch (const winrt::hresult_error&)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "pch.h"
|
||||
#include <common/utils/gpo.h>
|
||||
|
||||
class CSettings
|
||||
@@ -14,16 +15,10 @@ public:
|
||||
return true;
|
||||
if (gpoSetting == powertoys_gpo::gpo_rule_configured_disabled)
|
||||
return false;
|
||||
Reload();
|
||||
RefreshEnabledState();
|
||||
return settings.enabled;
|
||||
}
|
||||
|
||||
inline void SetEnabled(bool enabled)
|
||||
{
|
||||
settings.enabled = enabled;
|
||||
Save();
|
||||
}
|
||||
|
||||
void Save();
|
||||
void Load();
|
||||
|
||||
@@ -33,13 +28,16 @@ private:
|
||||
bool enabled{ true };
|
||||
};
|
||||
|
||||
void RefreshEnabledState();
|
||||
void Reload();
|
||||
void MigrateFromRegistry();
|
||||
void ParseJson();
|
||||
|
||||
Settings settings;
|
||||
std::wstring jsonFilePath;
|
||||
std::wstring generalJsonFilePath;
|
||||
FILETIME lastLoadedTime;
|
||||
FILETIME lastLoadedGeneralSettingsTime{};
|
||||
};
|
||||
|
||||
CSettings& CSettingsInstance();
|
||||