mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-01-29 07:27:27 +01:00
Compare commits
69 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc7d1bc7b2 | ||
|
|
26e8d0d976 | ||
|
|
acadd069e8 | ||
|
|
6e4a5a4bb6 | ||
|
|
6212c1bb80 | ||
|
|
3358fd9b02 | ||
|
|
88e24263cf | ||
|
|
d7098e87ee | ||
|
|
64ecb553e4 | ||
|
|
2130cef3a9 | ||
|
|
f647223e94 | ||
|
|
a0ebe5ed54 | ||
|
|
b05378cdf7 | ||
|
|
423faf7af1 | ||
|
|
f9bb7ba270 | ||
|
|
4bc2de7b7b | ||
|
|
d199b41937 | ||
|
|
7fe63cdb50 | ||
|
|
f4531130b6 | ||
|
|
c6a24b3907 | ||
|
|
2c44cc9cd2 | ||
|
|
739737dc2b | ||
|
|
6bd0840863 | ||
|
|
323f41d14b | ||
|
|
8115a709e9 | ||
|
|
b0344f1426 | ||
|
|
9fb663210d | ||
|
|
881b1da6a7 | ||
|
|
8e350ca4a7 | ||
|
|
a0a355c3a7 | ||
|
|
7ac1e00d01 | ||
|
|
7e22f26b52 | ||
|
|
4f02795c60 | ||
|
|
f430aff4af | ||
|
|
f05d64b01e | ||
|
|
cae6bdf437 | ||
|
|
7d8f7aafdd | ||
|
|
5d279ed22c | ||
|
|
1edd52014e | ||
|
|
1f2f247c1d | ||
|
|
7c2a807278 | ||
|
|
21c7374177 | ||
|
|
1495285a6a | ||
|
|
bdeb96d0a7 | ||
|
|
5963294b04 | ||
|
|
2c58bdbfb2 | ||
|
|
f00bf7cf19 | ||
|
|
2fe6282157 | ||
|
|
a6cca7cfb0 | ||
|
|
2bfc62d9a5 | ||
|
|
dcc4563c8c | ||
|
|
a4f84844bc | ||
|
|
941221cba9 | ||
|
|
0b6d654ec8 | ||
|
|
b8236d55e2 | ||
|
|
4a1e21ac83 | ||
|
|
ee69dc5e44 | ||
|
|
fd06c52dec | ||
|
|
91910b4606 | ||
|
|
f10faf004e | ||
|
|
f0750997de | ||
|
|
f7333c89bb | ||
|
|
19fb3fd56c | ||
|
|
d197ddeb15 | ||
|
|
46bfd2cdac | ||
|
|
45b5545cf7 | ||
|
|
e881043787 | ||
|
|
2b95eba51b | ||
|
|
4177708e49 |
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -39,6 +39,8 @@ body:
|
||||
- Image Resizer
|
||||
- Keyboard Manager
|
||||
- MD Preview
|
||||
- PDF Preview
|
||||
- PDF Thumbnail
|
||||
- PowerRename
|
||||
- PowerToys Run
|
||||
- Shortcut Guide
|
||||
|
||||
1
.github/actions/spell-check/excludes.txt
vendored
1
.github/actions/spell-check/excludes.txt
vendored
@@ -25,6 +25,7 @@ ignore$
|
||||
\.png$
|
||||
\.woff$
|
||||
\.zip$
|
||||
^doc/devdocs/akaLinks\.md$
|
||||
^src/common/logger/logger\.vcxproj\.filters$
|
||||
^src/common/notifications/BackgroundActivatorDLL/BackgroundActivator\.vcxproj\.filters$
|
||||
^src/common/notifications/BackgroundActivatorDLL/cpp\.hint$
|
||||
|
||||
29
.github/actions/spell-check/expect.txt
vendored
29
.github/actions/spell-check/expect.txt
vendored
@@ -12,10 +12,10 @@ Acceleratorkeys
|
||||
ACCEPTFILES
|
||||
accessibile
|
||||
accessibilityinsights
|
||||
Accessible
|
||||
Acl
|
||||
aclapi
|
||||
AColumn
|
||||
Accessible
|
||||
acos
|
||||
acrt
|
||||
Actioncenter
|
||||
@@ -103,6 +103,7 @@ asm
|
||||
asmx
|
||||
aspnet
|
||||
aspx
|
||||
ASSOCCHANGED
|
||||
ASYNCWINDOWPLACEMENT
|
||||
ASYNCWINDOWPOS
|
||||
atl
|
||||
@@ -125,8 +126,8 @@ AUTOMATIONPROPERTIES
|
||||
Autorun
|
||||
AUTOSIZECOLUMNS
|
||||
AUTOUPDATE
|
||||
available
|
||||
AValid
|
||||
avialable
|
||||
awakeversion
|
||||
AWAYMODE
|
||||
AYUV
|
||||
@@ -229,10 +230,13 @@ CHILDACTIVATE
|
||||
CHILDWINDOW
|
||||
chrdavis
|
||||
chrisharris
|
||||
chromaticities
|
||||
chrono
|
||||
Chrzan
|
||||
chrzan
|
||||
CHT
|
||||
cielab
|
||||
CIEXYZ
|
||||
CImage
|
||||
cinttypes
|
||||
cla
|
||||
@@ -288,6 +292,7 @@ comhost
|
||||
cominterop
|
||||
commandline
|
||||
commctrl
|
||||
companding
|
||||
Compat
|
||||
COMPOSITIONFULL
|
||||
comsupp
|
||||
@@ -374,7 +379,6 @@ CXSMICON
|
||||
CXVIRTUALSCREEN
|
||||
cxx
|
||||
cxxopts
|
||||
CYMK
|
||||
CYSMICON
|
||||
cziplib
|
||||
Dac
|
||||
@@ -395,6 +399,7 @@ dbg
|
||||
Dbghelp
|
||||
DBLCLKS
|
||||
DBLEPSILON
|
||||
dchristensen
|
||||
DCOM
|
||||
dcomp
|
||||
DComposition
|
||||
@@ -402,6 +407,7 @@ ddd
|
||||
ddee
|
||||
ddf
|
||||
Deact
|
||||
debian
|
||||
DECLAR
|
||||
declspec
|
||||
decltype
|
||||
@@ -650,6 +656,7 @@ FOF
|
||||
FOFX
|
||||
FOLDERID
|
||||
folderpath
|
||||
FORCEMINIMIZE
|
||||
FORCEOFFLINE
|
||||
foreach
|
||||
formatetc
|
||||
@@ -734,6 +741,7 @@ HDN
|
||||
hdrop
|
||||
HDS
|
||||
HEB
|
||||
Heiko
|
||||
helptext
|
||||
HEVC
|
||||
hfile
|
||||
@@ -806,6 +814,7 @@ Hyperlink
|
||||
IAction
|
||||
IActivated
|
||||
IAnimatable
|
||||
ianjoneill
|
||||
IApp
|
||||
IApplication
|
||||
IAppx
|
||||
@@ -842,6 +851,7 @@ IDirectory
|
||||
IDispatch
|
||||
IDisposable
|
||||
idl
|
||||
IDLIST
|
||||
IDOK
|
||||
IDOn
|
||||
IDR
|
||||
@@ -1026,6 +1036,7 @@ ixx
|
||||
IYUV
|
||||
IZone
|
||||
IZoom
|
||||
jakeoeding
|
||||
JArray
|
||||
jarro
|
||||
Jarryd
|
||||
@@ -1050,6 +1061,7 @@ jyuwono
|
||||
kbd
|
||||
KBDLLHOOKSTRUCT
|
||||
kbm
|
||||
KERNELBASE
|
||||
KEYBDINPUT
|
||||
keyboardeventhandlers
|
||||
keyboardmanager
|
||||
@@ -1258,6 +1270,7 @@ mii
|
||||
MIIM
|
||||
millis
|
||||
mimetype
|
||||
mindaro
|
||||
Minimizeallwindows
|
||||
MINIMIZEBOX
|
||||
miniz
|
||||
@@ -1314,6 +1327,7 @@ msiquery
|
||||
MSIRESTARTMANAGERCONTROL
|
||||
msix
|
||||
msixbundle
|
||||
MSIXCA
|
||||
MSIXVERSION
|
||||
MSLLHOOKSTRUCT
|
||||
Mso
|
||||
@@ -1445,6 +1459,7 @@ nuget
|
||||
null
|
||||
nullopt
|
||||
nullptr
|
||||
numberbox
|
||||
NUMLOCK
|
||||
NUMPAD
|
||||
nunit
|
||||
@@ -1527,6 +1542,7 @@ PCWSTR
|
||||
pdb
|
||||
pdbonly
|
||||
pdf
|
||||
pdfpreviewhandler
|
||||
pdo
|
||||
pdto
|
||||
pdtobj
|
||||
@@ -1849,6 +1865,8 @@ sfgao
|
||||
SFGAOF
|
||||
SHAREIMAGELISTS
|
||||
sharpkeys
|
||||
SHCNE
|
||||
SHCNF
|
||||
shcore
|
||||
shellapi
|
||||
SHELLDLL
|
||||
@@ -1881,7 +1899,9 @@ SHOWDEFAULT
|
||||
SHOWELEVATIONPROMPT
|
||||
SHOWMAXIMIZED
|
||||
SHOWMINIMIZED
|
||||
SHOWMINNOACTIVE
|
||||
SHOWNA
|
||||
SHOWNOACTIVATE
|
||||
SHOWNORMAL
|
||||
SHOWWINDOW
|
||||
shtypes
|
||||
@@ -1944,6 +1964,7 @@ SRCCOPY
|
||||
sre
|
||||
sregex
|
||||
SResize
|
||||
SRGB
|
||||
srme
|
||||
srre
|
||||
srw
|
||||
@@ -2103,6 +2124,7 @@ todo
|
||||
toggleswitch
|
||||
toolbar
|
||||
Toolchain
|
||||
toolkitcontrols
|
||||
toolkitconverters
|
||||
toolset
|
||||
toolstrip
|
||||
@@ -2120,6 +2142,7 @@ traies
|
||||
transcoded
|
||||
transparrent
|
||||
TRAYMOUSEMESSAGE
|
||||
triaging
|
||||
TRK
|
||||
trl
|
||||
trunc
|
||||
|
||||
@@ -9,7 +9,11 @@ jobs:
|
||||
variables:
|
||||
BuildConfiguration: ${{ parameters.configuration }}
|
||||
BuildPlatform: ${{ parameters.platform }}
|
||||
pool: "windevbuildagents"
|
||||
pool:
|
||||
${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPoolOSS-L
|
||||
${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPool-L
|
||||
timeoutInMinutes: 120
|
||||
strategy:
|
||||
maxParallel: 10
|
||||
|
||||
@@ -121,7 +121,10 @@ steps:
|
||||
testSelector: 'testAssemblies'
|
||||
testAssemblyVer2: |
|
||||
**\UnitTests-SvgThumbnailProvider.dll
|
||||
**\UnitTests-PdfThumbnailProvider.dll
|
||||
**\Microsoft.PowerToys.Settings.UI.UnitTests.dll
|
||||
**\UnitTests-MarkdownPreviewHandler.dll
|
||||
**\UnitTests-PdfPreviewHandler.dll
|
||||
**\UnitTests-SvgPreviewHandler.dll
|
||||
**\UnitTests-PreviewHandlerCommon.dll
|
||||
**\PreviewPaneUnitTests.dll
|
||||
|
||||
@@ -32,6 +32,9 @@ restore:
|
||||
- !!defaultcommand
|
||||
name: 'Restore Installer'
|
||||
command: '.pipelines\restore-installer.cmd'
|
||||
- !!defaultcommand
|
||||
name: 'Restore Installer BootStrapper'
|
||||
command: '.pipelines\restore-bootstrapper.cmd'
|
||||
- !!defaultcommand
|
||||
name: 'Restore Localization packages'
|
||||
command: '.pipelines\restore-localization.cmd'
|
||||
@@ -39,8 +42,6 @@ restore:
|
||||
name: 'Restore Tools packages'
|
||||
command: '.pipelines\restore-tools.cmd'
|
||||
|
||||
|
||||
|
||||
build:
|
||||
commands:
|
||||
# Localize the files before the Build PowerToys step to generate translated resx files from the lcl files
|
||||
@@ -108,6 +109,10 @@ build:
|
||||
- 'modules\FileExplorerPreview\ManagedTelemetry.dll'
|
||||
- 'modules\FileExplorerPreview\MarkdownPreviewHandler.dll'
|
||||
- 'modules\FileExplorerPreview\MarkdownPreviewHandler.comhost.dll'
|
||||
- 'modules\FileExplorerPreview\PdfPreviewHandler.dll'
|
||||
- 'modules\FileExplorerPreview\PdfPreviewHandler.comhost.dll'
|
||||
- 'modules\FileExplorerPreview\PdfThumbnailProvider.dll'
|
||||
- 'modules\FileExplorerPreview\PdfThumbnailProvider.comhost.dll'
|
||||
- 'modules\FileExplorerPreview\powerpreview.dll'
|
||||
- 'modules\FileExplorerPreview\PreviewHandlerCommon.dll'
|
||||
- 'modules\FileExplorerPreview\SvgPreviewHandler.dll'
|
||||
@@ -168,6 +173,7 @@ build:
|
||||
# TODO(yuyoyuppe): uncomment when VCM should be enabled
|
||||
#- 'modules\VideoConference\VideoConferenceModule.dll'
|
||||
#- 'modules\VideoConference\VideoConferenceProxyFilter_x64.dll'
|
||||
#- 'modules\VideoConference\VideoConferenceProxyFilter_x86.dll'
|
||||
- 'Settings\ManagedTelemetry.dll'
|
||||
- 'Settings\Microsoft.PowerToys.Settings.UI.exe'
|
||||
- 'Settings\Microsoft.PowerToys.Settings.UI.Lib.dll'
|
||||
@@ -223,7 +229,6 @@ build:
|
||||
signing_options:
|
||||
sign_inline: true # This does signing as soon as this command completes
|
||||
|
||||
|
||||
#package:
|
||||
# commands:
|
||||
# - !!buildcommand
|
||||
|
||||
3
.pipelines/restore-bootstrapper.cmd
Normal file
3
.pipelines/restore-bootstrapper.cmd
Normal file
@@ -0,0 +1,3 @@
|
||||
cd /D "%~dp0"
|
||||
|
||||
nuget restore ../installer/PowerToysBootstrapper/PowerToysBootstrapper.sln || exit /b 1
|
||||
@@ -9,6 +9,9 @@ Names are in alphabetical order based on first name.
|
||||
### [@davidegiacometti](https://github.com/davidegiacometti) - [Davide Giacometti](https://www.linkedin.com/in/davidegiacometti/)
|
||||
Davide has helped fix multiple bugs, added new features, as well as help us with the ARM64 effort by porting applications to .NET Core.
|
||||
|
||||
### [@htcfreek](https://github.com/htcfreek) - Heiko
|
||||
Heiko has helped triaging, discussing, and creating a substantial number of issues and contributed features/fixes to PowerToys Run.
|
||||
|
||||
### [@jsoref](https://github.com/jsoref) - [Josh Soref](https://check-spelling.dev/)
|
||||
Helping keep our spelling correct :)
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "runner", "src\runner\runner
|
||||
{0B593A6C-4143-4337-860E-DB5710FB87DB} = {0B593A6C-4143-4337-860E-DB5710FB87DB}
|
||||
{E364F67B-BB12-4E91-B639-355866EBCD8B} = {E364F67B-BB12-4E91-B639-355866EBCD8B}
|
||||
{D940E07F-532C-4FF3-883F-790DA014F19A} = {D940E07F-532C-4FF3-883F-790DA014F19A}
|
||||
{69E1EE8D-143A-4060-9129-4658ACF14AAF} = {69E1EE8D-143A-4060-9129-4658ACF14AAF}
|
||||
{DA425894-6E13-404F-8DCB-78584EC0557A} = {DA425894-6E13-404F-8DCB-78584EC0557A}
|
||||
{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34} = {2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}
|
||||
{A7D5099E-F0FD-4BF3-8522-5A682759F915} = {A7D5099E-F0FD-4BF3-8522-5A682759F915}
|
||||
@@ -166,7 +167,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PreviewHandlerCommon", "src
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MarkdownPreviewHandler", "src\modules\previewpane\MarkdownPreviewHandler\MarkdownPreviewHandler.csproj", "{6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests-MarkdownPreviewHandler", "src\modules\previewpane\PreviewPaneUnitTests\UnitTests-MarkdownPreviewHandler.csproj", "{A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests-MarkdownPreviewHandler", "src\modules\previewpane\UnitTests-MarkdownPreviewHandler\UnitTests-MarkdownPreviewHandler.csproj", "{A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SvgPreviewHandler", "src\modules\previewpane\SvgPreviewHandler\SvgPreviewHandler.csproj", "{DA425894-6E13-404F-8DCB-78584EC0557A}"
|
||||
EndProject
|
||||
@@ -305,6 +306,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Telemetry", "Telemetry", "{
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Common.UI", "src\common\Microsoft.PowerToys.Common.UI\Microsoft.PowerToys.Common.UI.csproj", "{C3A17DCA-217B-462C-BB0C-BE086AF80081}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PdfPreviewHandler", "src\modules\previewpane\PdfPreviewHandler\PdfPreviewHandler.csproj", "{69E1EE8D-143A-4060-9129-4658ACF14AAF}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests-PdfPreviewHandler", "src\modules\previewpane\UnitTests-PdfPreviewHandler\UnitTests-PdfPreviewHandler.csproj", "{ECC20689-002A-4354-95A6-B58DF089C6FF}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Run.Plugin.Registry", "src\modules\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.Registry\Microsoft.PowerToys.Run.Plugin.Registry.csproj", "{4BABF3FE-3451-42FD-873F-3C332E18DCEF}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Run.Plugin.Registry.UnitTests", "src\modules\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.Registry.UnitTest\Microsoft.PowerToys.Run.Plugin.Registry.UnitTests.csproj", "{0648DF05-5DDA-4BE1-B5F2-584926EBDB65}"
|
||||
@@ -358,6 +363,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VideoConferenceProxyFilter"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "VideoConference", "VideoConference", "{470FBAF9-E1F8-4F3E-8786-198A1C81C8A8}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PdfThumbnailProvider", "src\modules\previewpane\PdfThumbnailProvider\PdfThumbnailProvider.csproj", "{11491FD8-F921-48BF-880C-7FEA185B80A1}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests-PdfThumbnailProvider", "src\modules\previewpane\UnitTests-PdfThumbnailProvider\UnitTests-PdfThumbnailProvider.csproj", "{F40C3397-1834-4530-B2D9-8F8B8456BCDF}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
@@ -796,6 +805,18 @@ Global
|
||||
{C3A17DCA-217B-462C-BB0C-BE086AF80081}.Release|x64.ActiveCfg = Release|x64
|
||||
{C3A17DCA-217B-462C-BB0C-BE086AF80081}.Release|x64.Build.0 = Release|x64
|
||||
{C3A17DCA-217B-462C-BB0C-BE086AF80081}.Release|x86.ActiveCfg = Release|x64
|
||||
{69E1EE8D-143A-4060-9129-4658ACF14AAF}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{69E1EE8D-143A-4060-9129-4658ACF14AAF}.Debug|x64.Build.0 = Debug|x64
|
||||
{69E1EE8D-143A-4060-9129-4658ACF14AAF}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{69E1EE8D-143A-4060-9129-4658ACF14AAF}.Release|x64.ActiveCfg = Release|x64
|
||||
{69E1EE8D-143A-4060-9129-4658ACF14AAF}.Release|x64.Build.0 = Release|x64
|
||||
{69E1EE8D-143A-4060-9129-4658ACF14AAF}.Release|x86.ActiveCfg = Release|x64
|
||||
{ECC20689-002A-4354-95A6-B58DF089C6FF}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{ECC20689-002A-4354-95A6-B58DF089C6FF}.Debug|x64.Build.0 = Debug|x64
|
||||
{ECC20689-002A-4354-95A6-B58DF089C6FF}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{ECC20689-002A-4354-95A6-B58DF089C6FF}.Release|x64.ActiveCfg = Release|x64
|
||||
{ECC20689-002A-4354-95A6-B58DF089C6FF}.Release|x64.Build.0 = Release|x64
|
||||
{ECC20689-002A-4354-95A6-B58DF089C6FF}.Release|x86.ActiveCfg = Release|x64
|
||||
{4BABF3FE-3451-42FD-873F-3C332E18DCEF}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{4BABF3FE-3451-42FD-873F-3C332E18DCEF}.Debug|x64.Build.0 = Debug|x64
|
||||
{4BABF3FE-3451-42FD-873F-3C332E18DCEF}.Debug|x86.ActiveCfg = Debug|x64
|
||||
@@ -932,6 +953,18 @@ Global
|
||||
{AC2857B4-103D-4D6D-9740-926EBF785042}.Release|x64.Build.0 = Release|x64
|
||||
{AC2857B4-103D-4D6D-9740-926EBF785042}.Release|x86.ActiveCfg = Release|Win32
|
||||
{AC2857B4-103D-4D6D-9740-926EBF785042}.Release|x86.Build.0 = Release|Win32
|
||||
{11491FD8-F921-48BF-880C-7FEA185B80A1}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{11491FD8-F921-48BF-880C-7FEA185B80A1}.Debug|x64.Build.0 = Debug|x64
|
||||
{11491FD8-F921-48BF-880C-7FEA185B80A1}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{11491FD8-F921-48BF-880C-7FEA185B80A1}.Release|x64.ActiveCfg = Release|x64
|
||||
{11491FD8-F921-48BF-880C-7FEA185B80A1}.Release|x64.Build.0 = Release|x64
|
||||
{11491FD8-F921-48BF-880C-7FEA185B80A1}.Release|x86.ActiveCfg = Release|x64
|
||||
{F40C3397-1834-4530-B2D9-8F8B8456BCDF}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{F40C3397-1834-4530-B2D9-8F8B8456BCDF}.Debug|x64.Build.0 = Debug|x64
|
||||
{F40C3397-1834-4530-B2D9-8F8B8456BCDF}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{F40C3397-1834-4530-B2D9-8F8B8456BCDF}.Release|x64.ActiveCfg = Release|x64
|
||||
{F40C3397-1834-4530-B2D9-8F8B8456BCDF}.Release|x64.Build.0 = Release|x64
|
||||
{F40C3397-1834-4530-B2D9-8F8B8456BCDF}.Release|x86.ActiveCfg = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -1019,6 +1052,8 @@ Global
|
||||
{B39DC643-4663-475E-B329-03F0C9918D48} = {1AFB6476-670D-4E80-A464-657E01DFF482}
|
||||
{8F62026A-294B-41C6-8839-87463613F216} = {1AFB6476-670D-4E80-A464-657E01DFF482}
|
||||
{C3A17DCA-217B-462C-BB0C-BE086AF80081} = {1AFB6476-670D-4E80-A464-657E01DFF482}
|
||||
{69E1EE8D-143A-4060-9129-4658ACF14AAF} = {2F305555-C296-497E-AC20-5FA1B237996A}
|
||||
{ECC20689-002A-4354-95A6-B58DF089C6FF} = {2F305555-C296-497E-AC20-5FA1B237996A}
|
||||
{4BABF3FE-3451-42FD-873F-3C332E18DCEF} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
|
||||
{0648DF05-5DDA-4BE1-B5F2-584926EBDB65} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
|
||||
{6ED2F4FC-E122-4CEE-90F1-97E4CCC8BC7A} = {C3081D9A-1586-441A-B5F4-ED815B3719C1}
|
||||
@@ -1043,6 +1078,8 @@ Global
|
||||
{5ABA70DE-3A3F-41F6-A1F5-D1F74F54F9BB} = {470FBAF9-E1F8-4F3E-8786-198A1C81C8A8}
|
||||
{AC2857B4-103D-4D6D-9740-926EBF785042} = {470FBAF9-E1F8-4F3E-8786-198A1C81C8A8}
|
||||
{470FBAF9-E1F8-4F3E-8786-198A1C81C8A8} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
|
||||
{11491FD8-F921-48BF-880C-7FEA185B80A1} = {2F305555-C296-497E-AC20-5FA1B237996A}
|
||||
{F40C3397-1834-4530-B2D9-8F8B8456BCDF} = {2F305555-C296-497E-AC20-5FA1B237996A}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
|
||||
|
||||
121
README.md
121
README.md
@@ -25,20 +25,23 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline
|
||||
|
||||
### Requirements
|
||||
|
||||
- Windows 10 v1903 (build 18362) or newer.
|
||||
- ⚠️ PowerToys (v0.37.0 and newer) requires Windows 10 v1903 (18362) or newer.
|
||||
- ⚠️ PowerToys (v0.37.0 and newer) requires Windows 10 v1903 (18362) or newer.
|
||||
|
||||
- Have [.NET Core 3.1.15 Desktop Runtime](https://dotnet.microsoft.com/download/dotnet/thank-you/runtime-desktop-3.1.15-windows-x64-installer). The installer should handle this but we want to directly make people aware.
|
||||
|
||||
### Via GitHub with EXE [Recommended]
|
||||
|
||||
#### Stable version
|
||||
|
||||
Install from the [Microsoft PowerToys GitHub releases page][github-release-link]. Click on `Assets` to show the files available in the release and then click on `PowerToysSetup-0.41.2-x64.exe` to download the PowerToys installer.
|
||||
Install from the [Microsoft Store's PowerToys page][microsoft-store-link] or use [Microsoft PowerToys GitHub releases page][github-release-link].
|
||||
|
||||
- For GitHub, click on `Assets` to show the files available in the release and then click on `PowerToysSetup-0.45.0-x64.exe` to download the PowerToys installer.
|
||||
- For Microsoft Store, you must be using the [new Microsoft Store](https://blogs.windows.com/windowsExperience/2021/06/24/building-a-new-open-microsoft-store-on-windows-11/) which will be available for both Windows 11 and Windows 10.
|
||||
|
||||
This is our preferred method.
|
||||
|
||||
#### Experimental version
|
||||
To install the Video Conference mute, please use the [v0.36 experimental version of PowerToys][github-prerelease-link] to try out this version. It includes all improvements from v0.35 in addition to the Video conference utility. Click on `Assets` to show the files available in the release and then download the .exe installer.
|
||||
To install the Video Conference mute, please use the [v0.46 experimental version of PowerToys][github-prerelease-link] to try out this version. It includes all improvements from v0.45 in addition to the Video conference utility. Click on `Assets` to show the files available in the release and then download the .exe installer.
|
||||
|
||||
### Via WinGet (Preview)
|
||||
Download PowerToys from [WinGet](https://github.com/microsoft/winget-cli#installing-the-client). To install PowerToys, run the following command from the command line / PowerShell:
|
||||
@@ -75,77 +78,114 @@ 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.43 - July 2021 Update
|
||||
### 0.45 - August 2021 Update
|
||||
|
||||
Our goals for the [v0.43 release cycle](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen+is%3Aissue+project%3Amicrosoft%2FPowerToys%2F22) primarily centered around stability updates and optimizations, installer updates, general bug fixes, and accessibility improvements.
|
||||
Our goals for the [v0.45 release cycle](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen+is%3Aissue+project%3Amicrosoft%2FPowerToys%2F23) primarily centered around stability updates and optimizations, installer updates, general bug fixes, and accessibility improvements.
|
||||
|
||||
An experimental version of PowerToys (v0.44) will be released the 2nd week of August and will include an updated version of Video Conference Mute.
|
||||
We also worked extensively with the community to build an updated settings menu UI. This UI update takes advantage of the latest styling elements to provide users with a refreshed, modern experience navigating the various utilities and their settings.
|
||||
|
||||
#### Highlights from v0.43
|
||||
An experimental version of [PowerToys (v0.46)][github-prerelease-link] was released the week of September 13th. Download and install it to try out the latest improvements to our Video Conference Mute utility! All updates from the v0.45.0 release will still apply in v0.46.0.
|
||||
|
||||
#### Highlights from v0.45
|
||||
|
||||
**General**
|
||||
|
||||
- New UI for sizes list view in Image Resizer settings. Thanks @niels9001!
|
||||
- Fixed FileInUse errors during install/update scenarios.
|
||||
- Fixed toggle switches on PowerToys run settings to display correctly.
|
||||
- Fixed header text not updating when theme color is changed. Thanks @niels9001!
|
||||
- Changed tooltip text for systray icon to be on a single line for Windows 11 compatibility.
|
||||
- Expanded the Report Bug tool to collect more robust diagnostic information.
|
||||
- Fixed screen reader functionality to stop announcing hidden text in settings.
|
||||
- Settings and OOBE windows updated with Fluent UX! We hope you enjoy the new modern feel of the application menus as we align our product with upcoming Windows 11 interfaces. Special thanks to @niels9001 for driving the development of this UI, along with many thanks to the various community members who offered constant feedback and adjustments to make this a truly spectacular update!
|
||||
- Added button to settings which displays version history. Thanks @niels9001!
|
||||
- Signed PowerToysSetupCustomActions.dll.
|
||||
- Improved auto-update experience. Thanks @niels9001!
|
||||
- Aligned OOBE theme color with Settings theme color. Thanks @niels9001!
|
||||
- Adjusted labeling of "Restart as Administrator" button to "Restart PowerToys as Administrator" to avoid ambiguity in meaning. Thanks @niels9001!
|
||||
- Added colored icons to settings sidebar. Thanks @niels9001!
|
||||
- Fixed accessibility issue in OOBE where Microsoft Docs and PowerToys release notes links could not be navigated to via keyboard. Thanks @niels9001!
|
||||
- Fixed settings header alignment. Thanks @niels9001!
|
||||
- Fixed text under updates section to be visible when in light mode. Thanks @niels9001!
|
||||
- Updated "Learn More" text to be more descriptive. Thanks @niels9001!
|
||||
- Updated "Read more" text on updates to be more descriptive. Thanks @niels9001!
|
||||
- Added link to documentation in system tray. Thanks @BenConstable9!
|
||||
- Fixed error caused by file in use issues when installing PowerToys.
|
||||
- Fixed issue where opening settings from start menu didn't work when PowerToys was run as admin. Thanks @davidegiacometti!
|
||||
|
||||
### Awake
|
||||
|
||||
- Fixed bug when right-clicking menu of Awake app icon. Thanks @dend!
|
||||
- Fixed high CPU usage for timed keep awake. Thanks @dend!
|
||||
- Fixed Awake icon spamming notification tray. Thanks @dend!
|
||||
- Added telemetry to collect Awake settings and logs.
|
||||
- Added PowerToys Awake as option in translation bug template. Thanks @Aaron-Junker!
|
||||
- Adjusted description of inactive setting to improve distinguishing between the utility being disabled vs inactive. Thanks @niels9001!
|
||||
|
||||
### Color Picker
|
||||
|
||||
- Fixed escape behavior so that only the fly-out is closed if active.
|
||||
- Fixed several accessibility issues related to screen reader functionality and general usage of Color Picker.
|
||||
- Fixed bug where changing RGB values doesn't update color's HEX value. Thanks @martinchrzan!
|
||||
- Fixed accessibility issue with screen reader not announcing when "Copied to Clipboard" is activated.
|
||||
- Fixed accessibility issue where user could not hover the content of the info icon using a mouse. Thanks @niels9001!
|
||||
- Fixed color picker format order not being accessible via keyboard. Thanks @niels9001!
|
||||
- Fixed accessibility issue where screen reader announces incorrect name for "Editor color format" button and not announcing "Toggle switch" button at all. Thanks @niels9001!
|
||||
|
||||
### FancyZones
|
||||
|
||||
- Fixed bug causing multi-monitor spanning errors.
|
||||
- Added minimum zone size limit to the settings.
|
||||
- Fixed issue where re-opened windows don't appear in previously assigned zone.
|
||||
- Fixed excluded apps setting to save on text change instead of when leaving focus.
|
||||
- Fixed corrupt/outdated plugins load crash.
|
||||
- Fixed issue with FancyZones not working after computer goes to sleep.
|
||||
- Added screen reader confirmation to canvas editor when new zones are added.
|
||||
- Adjusted "Save and apply" editor button to adjust with text size for localizations. Thanks @niels9001!
|
||||
- Fixed "Create new layout" button visibility when in high contrast mode. Thanks @niels9001!
|
||||
- Fixed scaling quirks related to editor UI. Thanks @niels9001!
|
||||
- Fixed editor crashing when double clicking the "edit layout" button.
|
||||
- Fixed issue with editor crashing immediately after displaying zones.
|
||||
- Fixed bug when navigating editor options via keyboard where pressing enter on unselected Canvas option launches Grid editor instead.
|
||||
- Fixed issue where FancyZones would not restore Console Applications.
|
||||
- Fixed Canvas editor and Grid editor window heights. Thanks @niels9001!
|
||||
- Fixed crash due to KERNELBASE.dll.
|
||||
- Fixed FancyZone icons to be smoother at higher DPI settings. Thanks @niels9001!
|
||||
- Fixed crash when changing between zone layouts.
|
||||
- Fixed regression where FancyZones does not resize windows on layout change.
|
||||
- Adjusted layout settings to reset shortcut key after canceling changes on a particular layout.
|
||||
|
||||
### File Explorer add-ons
|
||||
|
||||
- Fixed issue where markdown files were still previewed even when "Enable Markdown" was turned off.
|
||||
|
||||
### Image Resizer
|
||||
|
||||
- Added warning that GIF files with animations may not correctly resize if the encoding used for the files is incompatible.
|
||||
|
||||
### Keyboard Manager
|
||||
|
||||
- Fixed screen reader usage bugs to increase intuitiveness.
|
||||
- Improved UI for KBM re-mappings list. Thanks @niels9001!
|
||||
|
||||
### PowerRename
|
||||
|
||||
- Expanding a plugin option in settings can now be toggled. Thanks @niels9001!
|
||||
- Fixed race condition causing PowerRename to crash File Explorer. Thanks @ianjoneill!
|
||||
|
||||
### PowerToys Run
|
||||
|
||||
- Fixed crashing bug due to missing image file app.dark.png.
|
||||
- Fixed URI plugin bug with handling numeric input. Thanks @davidegiacometti!
|
||||
- Improved launch performance of PowerToys run on first call. Thanks @davidegiacometti!
|
||||
- Changed URI plugin to launch HTTPS by default instead of HTTP. Thanks @chrisharris333!
|
||||
- Added confirmation dialog when system commands are executed from PowerToys Run. Thanks @chrisharris333!
|
||||
- Fixed lag caused from PowerToys running in background and invoking Alt-Tab.
|
||||
- Resolved file not found exception when loading "System.Windows.Controls.Ribbon".
|
||||
- Fixed null reference exception crash.
|
||||
- Fixed registry plugin load crash.
|
||||
- Fixed unauthorized access exception crash when setting registry keys for the utility.
|
||||
- Added search for Plugin Manager. Thanks @davidegiacometti!
|
||||
- Fixed VSCode workspace plugin not working. Thanks @BenConstable9!
|
||||
|
||||
### Video Conference Mute
|
||||
|
||||
- Fixed toolbar top right vertical offset to allow users to close other app windows.
|
||||
- Fixed compatibility issues for certain systems when compiling from source.
|
||||
- Fixed toolbox from persisting on screen.
|
||||
- Fixed microphone un-muting when changing Video Conference Mute toolbar position.
|
||||
- Added Video Conference Mute to OOBE.
|
||||
|
||||
## Community contributions
|
||||
|
||||
We'd like to directly mention certain contributors (in alphabetical order) for their continued community support this month and helping directly make PowerToys a better piece of software.
|
||||
|
||||
[@Aaron-Junker](https://github.com/Aaron-Junker), [@chrisharris333](https://github.com/chrisharris333), [@davidegiacometti](https://github.com/davidegiacometti), [@dend](https://github.com/dend), [@franky920920](https://github.com/franky920920) [@htcfreek](https://github.com/htcfreek), [@Jay-o-Way](https://github.com/Jay-o-Way), [@jsoref](https://github.com/jsoref), [@niels9001](https://github.com/niels9001), [@royvou](https://github.com/royvou), and [@tony-xia](https://github.com/tony-xia)
|
||||
[@Aaron-Junker](https://github.com/Aaron-Junker), [@BenConstable9](https://github.com/BenConstable9), [@davidegiacometti](https://github.com/davidegiacometti), [@dchristensen](https://github.com/dchristensen), [@dend](https://github.com/dend), [@franky920920](https://github.com/franky920920), [@htcfreek](https://github.com/htcfreek), [@ianjoneill](https://github.com/ianjoneill), [@jakeoeding](https://github.com/jakeoeding), [@Jay-o-Way](https://github.com/Jay-o-Way), [@jsoref](https://github.com/jsoref), [@martinchrzan](https://github.com/martinchrzan), [@niels9001](https://github.com/niels9001) and [@royvou](https://github.com/royvou)
|
||||
|
||||
#### What is being planned for v0.45
|
||||
#### What is being planned for v0.47
|
||||
|
||||
For [v0.45][github-next-release-work], we are planning to work on:
|
||||
For [v0.47][github-next-release-work], we are planning to work on:
|
||||
|
||||
- Stability and bug fixes
|
||||
- Installer improvements
|
||||
- Upgrading PowerToys Run to .NET 5
|
||||
- Preliminary UI/UX investigations to adopt WinUI and improve accessibility
|
||||
- Configuring Shortcut guide to re-enable long `Win` key press to activate
|
||||
- Testing PDF preview functionality for File Explorer add-ons
|
||||
- Planning for new utilities and enhancements
|
||||
|
||||
## PowerToys Community
|
||||
|
||||
@@ -162,7 +202,8 @@ The application logs basic telemetry. Our Telemetry Data page (Coming Soon) has
|
||||
[oss-CLA]: https://cla.opensource.microsoft.com
|
||||
[oss-conduct-code]: CODE_OF_CONDUCT.md
|
||||
[community-link]: COMMUNITY.md
|
||||
[github-release-link]: https://github.com/microsoft/PowerToys/releases/
|
||||
[github-release-link]: https://aka.ms/installPowerToys
|
||||
[microsoft-store-link]: https://aka.ms/getPowertoys
|
||||
[roadmap]: https://github.com/microsoft/PowerToys/wiki/Roadmap
|
||||
[privacy-link]: http://go.microsoft.com/fwlink/?LinkId=521839
|
||||
[vidConfOverview]: https://aka.ms/PowerToysOverview_VideoConference
|
||||
@@ -171,4 +212,4 @@ The application logs basic telemetry. Our Telemetry Data page (Coming Soon) has
|
||||
|
||||
<!-- items that need to be updated release to release -->
|
||||
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aopen+is%3Aissue+project%3Amicrosoft%2FPowerToys%2F21
|
||||
[github-prerelease-link]: https://github.com/microsoft/PowerToys/releases/tag/v0.36.0
|
||||
[github-prerelease-link]: https://github.com/microsoft/PowerToys/releases/tag/v0.46.0
|
||||
|
||||
2
deps/cziplib
vendored
2
deps/cziplib
vendored
Submodule deps/cziplib updated: f9e0959eb2...692cbcf8ca
@@ -2,7 +2,9 @@
|
||||
|
||||
| ShortUrl | TargetUrl |
|
||||
|----------|----------|
|
||||
| getpowertoys | ms-windows-store://pdp/?productid=XP89DCGQ3K6VLD |
|
||||
| installpowertoys | https://github.com/microsoft/PowerToys/releases/latest |
|
||||
| powertoys-license | https://github.com/microsoft/PowerToys/blob/master/LICENSE |
|
||||
| powertoys | https://github.com/microsoft/PowerToys |
|
||||
| PowerToysAppCompat | https://github.com/microsoft/PowerToys/wiki/Application-Compatibility |
|
||||
| powerToysCannotRemapKeys | https://docs.microsoft.com/windows/powertoys/keyboard-manager#keys-that-cannot-be-remapped |
|
||||
|
||||
BIN
doc/images/icons/PowerToys icon/PNG/1440x2160.png
Normal file
BIN
doc/images/icons/PowerToys icon/PNG/1440x2160.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 206 KiB |
BIN
doc/images/icons/PowerToys icon/PNG/2160x2160.png
Normal file
BIN
doc/images/icons/PowerToys icon/PNG/2160x2160.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 282 KiB |
@@ -79,11 +79,20 @@
|
||||
<desktop2:DesktopPreviewHandler Clsid="74619BDA-A66B-451D-864C-A7726F5FE650"/>
|
||||
</uap3:FileTypeAssociation>
|
||||
</uap:Extension>
|
||||
<uap:Extension Category="windows.fileTypeAssociation">
|
||||
<uap3:FileTypeAssociation Name="pdfpreviewhandler" desktop2:AllowSilentDefaultTakeOver="true">
|
||||
<uap:SupportedFileTypes>
|
||||
<uap:FileType>.pdf</uap:FileType>
|
||||
</uap:SupportedFileTypes>
|
||||
<desktop2:DesktopPreviewHandler Clsid="4F6D533B-4185-43A6-AD75-9B20034B14CA"/>
|
||||
</uap3:FileTypeAssociation>
|
||||
</uap:Extension>
|
||||
<com:Extension Category="windows.comServer">
|
||||
<com:ComServer>
|
||||
<com:SurrogateServer DisplayName="Preview Handler" AppId="E39A92FE-D89A-417B-9B9D-F0B6BD564B36" SystemSurrogate="PreviewHost">
|
||||
<com:Class Id="74619BDA-A66B-451D-864C-A7726F5FE650" Path="modules\powerpreview.dll" ThreadingModel="Both"/>
|
||||
<com:Class Id="E0907A95-6F9A-4D1B-A97A-7D9D2648881E" Path="modules\powerpreview.dll" ThreadingModel="Both"/>
|
||||
<com:Class Id="4F6D533B-4185-43A6-AD75-9B20034B14CA" Path="modules\powerpreview.dll" ThreadingModel="Both"/>
|
||||
</com:SurrogateServer>
|
||||
</com:ComServer>
|
||||
</com:Extension>
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
#include "pch.h"
|
||||
#include "RcResource.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
std::optional<RcResource> RcResource::create(int resource_id, const std::wstring_view resource_class)
|
||||
{
|
||||
const HRSRC resHandle = FindResourceW(nullptr, MAKEINTRESOURCEW(resource_id), resource_class.data());
|
||||
if (!resHandle)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const HGLOBAL memHandle = LoadResource(nullptr, resHandle);
|
||||
if (!memHandle)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const size_t resSize = SizeofResource(nullptr, resHandle);
|
||||
if (!resSize)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto res = static_cast<const std::byte*>(LockResource(memHandle));
|
||||
if (!res)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return RcResource{ res, resSize };
|
||||
}
|
||||
|
||||
bool RcResource::saveAsFile(const std::filesystem::path destination)
|
||||
{
|
||||
std::fstream installerFile{ destination, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc };
|
||||
if (!installerFile.is_open())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
installerFile.write(reinterpret_cast<const char*>(_memory), _size);
|
||||
return true;
|
||||
}
|
||||
@@ -3,6 +3,9 @@
|
||||
#include <string_view>
|
||||
#include <optional>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
class RcResource
|
||||
{
|
||||
@@ -10,8 +13,46 @@ public:
|
||||
const std::byte* _memory = nullptr;
|
||||
size_t _size = 0;
|
||||
|
||||
static std::optional<RcResource> create(int resource_id, const std::wstring_view resource_class);
|
||||
bool saveAsFile(const std::filesystem::path destination);
|
||||
static inline std::optional<RcResource> create(int resource_id, const std::wstring_view resource_class, const HINSTANCE handle = nullptr)
|
||||
{
|
||||
const HRSRC resHandle = FindResourceW(handle, MAKEINTRESOURCEW(resource_id), resource_class.data());
|
||||
if (!resHandle)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const HGLOBAL memHandle = LoadResource(handle, resHandle);
|
||||
if (!memHandle)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const size_t resSize = SizeofResource(handle, resHandle);
|
||||
if (!resSize)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto res = static_cast<const std::byte*>(LockResource(memHandle));
|
||||
if (!res)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return RcResource{ res, resSize };
|
||||
}
|
||||
|
||||
inline bool saveAsFile(const std::filesystem::path destination)
|
||||
{
|
||||
std::fstream installerFile{ destination, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc };
|
||||
if (!installerFile.is_open())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
installerFile.write(reinterpret_cast<const char*>(_memory), _size);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
RcResource() = delete;
|
||||
|
||||
@@ -121,17 +121,22 @@ bool uninstall_msi_version(const std::wstring& package_path)
|
||||
return ERROR_SUCCESS == uninstall_result;
|
||||
}
|
||||
|
||||
std::optional<VersionHelper> get_installed_powertoys_version()
|
||||
struct InstalledVersionInfo
|
||||
{
|
||||
VersionHelper version;
|
||||
std::wstring install_folder;
|
||||
};
|
||||
std::optional<InstalledVersionInfo> get_installed_powertoys_version()
|
||||
{
|
||||
auto installed_path = GetMsiPackageInstalledPath();
|
||||
if (!installed_path)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
*installed_path += L"\\PowerToys.exe";
|
||||
std::wstring executable_path = *installed_path + L"\\PowerToys.exe";
|
||||
|
||||
// Get the version information for the file requested
|
||||
const DWORD fvSize = GetFileVersionInfoSizeW(installed_path->c_str(), nullptr);
|
||||
const DWORD fvSize = GetFileVersionInfoSizeW(executable_path.c_str(), nullptr);
|
||||
if (!fvSize)
|
||||
{
|
||||
return std::nullopt;
|
||||
@@ -139,7 +144,7 @@ std::optional<VersionHelper> get_installed_powertoys_version()
|
||||
|
||||
auto pbVersionInfo = std::make_unique<BYTE[]>(fvSize);
|
||||
|
||||
if (!GetFileVersionInfoW(installed_path->c_str(), 0, fvSize, pbVersionInfo.get()))
|
||||
if (!GetFileVersionInfoW(executable_path.c_str(), 0, fvSize, pbVersionInfo.get()))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -150,23 +155,17 @@ std::optional<VersionHelper> get_installed_powertoys_version()
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
return VersionHelper{ (fileInfo->dwFileVersionMS >> 16) & 0xffff,
|
||||
(fileInfo->dwFileVersionMS >> 0) & 0xffff,
|
||||
(fileInfo->dwFileVersionLS >> 16) & 0xffff };
|
||||
return InstalledVersionInfo{
|
||||
.version = VersionHelper{ (fileInfo->dwFileVersionMS >> 16) & 0xffff,
|
||||
(fileInfo->dwFileVersionMS >> 0) & 0xffff,
|
||||
(fileInfo->dwFileVersionLS >> 16) & 0xffff },
|
||||
.install_folder = std::move(*installed_path)
|
||||
};
|
||||
}
|
||||
|
||||
int Bootstrapper(HINSTANCE hInstance)
|
||||
{
|
||||
winrt::init_apartment();
|
||||
char* programFilesDir = nullptr;
|
||||
size_t size = 0;
|
||||
std::string defaultInstallDir;
|
||||
|
||||
if (!_dupenv_s(&programFilesDir, &size, "PROGRAMFILES"))
|
||||
{
|
||||
defaultInstallDir += programFilesDir;
|
||||
defaultInstallDir += "\\PowerToys";
|
||||
}
|
||||
|
||||
fs::path logDir = PTSettingsHelper::get_root_save_folder_location();
|
||||
|
||||
@@ -182,7 +181,7 @@ int Bootstrapper(HINSTANCE hInstance)
|
||||
("skip_dotnet_install", "Skip dotnet 3.X installation even if it's not detected")
|
||||
("log_level", "Log level. Possible values: off|debug|error", cxxopts::value<std::string>()->default_value("off"))
|
||||
("log_dir", "Log directory", cxxopts::value<std::string>()->default_value(logDir.string()))
|
||||
("install_dir", "Installation directory", cxxopts::value<std::string>()->default_value(defaultInstallDir))
|
||||
("install_dir", "Installation directory", cxxopts::value<std::string>()->default_value(""))
|
||||
("extract_msi", "Extract MSI to the working directory and exit. Use only if you must access MSI directly.");
|
||||
// clang-format on
|
||||
|
||||
@@ -231,7 +230,7 @@ int Bootstrapper(HINSTANCE hInstance)
|
||||
|
||||
installFolderProp = std::wstring(installDir.length(), L' ');
|
||||
std::copy(installDir.begin(), installDir.end(), installFolderProp.begin());
|
||||
installFolderProp = L"INSTALLFOLDER=" + installFolderProp;
|
||||
installFolderProp = L"INSTALLFOLDER=\"" + installFolderProp + L"\"";
|
||||
}
|
||||
|
||||
try
|
||||
@@ -281,12 +280,21 @@ int Bootstrapper(HINSTANCE hInstance)
|
||||
}
|
||||
|
||||
// Check if there's a newer version installed
|
||||
const auto installedVersion = get_installed_powertoys_version();
|
||||
if (installedVersion && *installedVersion >= myVersion)
|
||||
const auto installedVersionInfo = get_installed_powertoys_version();
|
||||
if (installedVersionInfo)
|
||||
{
|
||||
spdlog::error(L"Detected a newer version {} vs {}", (*installedVersion).toWstring(), myVersion.toWstring());
|
||||
ShowMessageBoxError(IDS_NEWER_VERSION_ERROR);
|
||||
return 0;
|
||||
if (installedVersionInfo->version >= myVersion)
|
||||
{
|
||||
spdlog::error(L"Detected a newer version {} vs {}", installedVersionInfo->version.toWstring(), myVersion.toWstring());
|
||||
ShowMessageBoxError(IDS_NEWER_VERSION_ERROR);
|
||||
return 0;
|
||||
}
|
||||
// If we are good to go and install folder wasn't specified via cmd line, make sure to retain the previous
|
||||
// installation path
|
||||
else if (installFolderProp.empty())
|
||||
{
|
||||
installFolderProp = L"INSTALLFOLDER=\"" + installedVersionInfo->install_folder + L"\"";
|
||||
}
|
||||
}
|
||||
|
||||
// Setup MSI UI visibility and restart as elevated if required
|
||||
@@ -392,7 +400,7 @@ int Bootstrapper(HINSTANCE hInstance)
|
||||
|
||||
if (!package_path.empty() && !uninstall_msi_version(package_path))
|
||||
{
|
||||
spdlog::error("Couldn't install the existing MSI package ({})", GetLastError());
|
||||
spdlog::error("Couldn't uninstall the existing MSI package ({})", GetLastError());
|
||||
ShowMessageBoxError(IDS_UNINSTALL_PREVIOUS_VERSION_ERROR);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -119,18 +119,15 @@
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="progressbar_window.cpp" />
|
||||
<ClCompile Include="RcResource.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="DotnetInstallation.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="progressbar_window.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="bootstrapper.base.rc" />
|
||||
<None Include="packages.config" />
|
||||
<ResourceCompile Include="Generated Files/bootstrapper.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -149,12 +146,12 @@
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
<Import Project="..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.200902.2" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.210204.1" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -94,13 +94,20 @@
|
||||
</Custom>
|
||||
<Custom Action="WixCloseApplications" Before="RemoveFiles" />
|
||||
<Custom Action="RemovePowerToysSchTasks" After="RemoveFiles" />
|
||||
<!-- TODO: Use to activate embedded MSIX -->
|
||||
<!--<Custom Action="InstallEmbeddedMSIXTask" After="InstallFinalize">
|
||||
NOT Installed
|
||||
</Custom>-->
|
||||
<Custom Action="TelemetryLogInstallSuccess" After="InstallFinalize">
|
||||
NOT Installed
|
||||
</Custom>
|
||||
<Custom Action="TelemetryLogUninstallSuccess" After="InstallFinalize">
|
||||
Installed and (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")
|
||||
</Custom>
|
||||
|
||||
<!-- TODO: Use to activate embedded MSIX -->
|
||||
<!--<Custom Action="UninstallEmbeddedMSIXTask" After="InstallFinalize">
|
||||
Installed AND (REMOVE="ALL")
|
||||
</Custom>-->
|
||||
<Custom Action="TerminateProcesses" Before="InstallValidate" />
|
||||
|
||||
</InstallExecuteSequence>
|
||||
@@ -133,7 +140,21 @@
|
||||
DllEntry="RemoveScheduledTasksCA"
|
||||
/>
|
||||
|
||||
<CustomAction Id="TelemetryLogInstallSuccess"
|
||||
<CustomAction Id="InstallEmbeddedMSIXTask"
|
||||
Return="ignore"
|
||||
Impersonate="yes"
|
||||
BinaryKey="PTCustomActions"
|
||||
DllEntry="InstallEmbeddedMSIXCA"
|
||||
/>
|
||||
|
||||
<CustomAction Id="UninstallEmbeddedMSIXTask"
|
||||
Return="ignore"
|
||||
Impersonate="yes"
|
||||
BinaryKey="PTCustomActions"
|
||||
DllEntry="UninstallEmbeddedMSIXCA"
|
||||
/>
|
||||
|
||||
<CustomAction Id="TelemetryLogInstallSuccess"
|
||||
Return="ignore"
|
||||
Impersonate="yes"
|
||||
BinaryKey="PTCustomActions"
|
||||
@@ -239,7 +260,6 @@
|
||||
</Directory>
|
||||
</Directory>
|
||||
|
||||
|
||||
<!-- KBM -->
|
||||
<Directory Id="KeyboardManagerInstallFolder" Name="$(var.KeyboardManagerProjectName)">
|
||||
<Directory Id="KeyboardManagerEditorInstallFolder" Name="KeyboardManagerEditor" />
|
||||
@@ -499,6 +519,32 @@
|
||||
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="Assembly" Value="MarkdownPreviewHandler, Version=$(var.Version).0, Culture=neutral" />
|
||||
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="Class" Value="Microsoft.PowerToys.PreviewHandler.Markdown.MarkdownPreviewHandler" />
|
||||
</RegistryKey>
|
||||
<!-- Registry Key for Class Registration of Pdf Preview Handler -->
|
||||
<RegistryKey Root="HKCR" Key="CLSID\{07665729-6243-4746-95b7-79579308d1b2}">
|
||||
<RegistryValue Type="string" Value="Microsoft.PowerToys.PreviewHandler.Pdf.PdfPreviewHandler" />
|
||||
<RegistryValue Type="string" Name="DisplayName" Value="Pdf Preview Handler" />
|
||||
<RegistryValue Type="string" Name="AppID" Value="{CF142243-F059-45AF-8842-DBBE9783DB14}" />
|
||||
<RegistryValue Type="string" Key="Implemented Categories\{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}" Value="" />
|
||||
<RegistryValue Type="string" Key="InprocServer32" Value="[FileExplorerPreviewInstallFolder]PdfPreviewHandler.comhost.dll" />
|
||||
<RegistryValue Type="string" Key="InprocServer32" Name="Assembly" Value="PdfPreviewHandler, Version=$(var.Version).0, Culture=neutral" />
|
||||
<RegistryValue Type="string" Key="InprocServer32" Name="Class" Value="Microsoft.PowerToys.PreviewHandler.Pdf.PdfPreviewHandler" />
|
||||
<RegistryValue Type="string" Key="InprocServer32" Name="ThreadingModel" Value="Both" />
|
||||
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="Assembly" Value="PdfPreviewHandler, Version=$(var.Version).0, Culture=neutral" />
|
||||
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="Class" Value="Microsoft.PowerToys.PreviewHandler.Pdf.PdfPreviewHandler" />
|
||||
</RegistryKey>
|
||||
<!-- Registry Key for Class Registration of Pdf Thumbnail Provider -->
|
||||
<RegistryKey Root="HKCR" Key="CLSID\{BCC13D15-9720-4CC4-8371-EA74A274741E}">
|
||||
<RegistryValue Type="string" Value="Microsoft.PowerToys.ThumbnailHandler.Pdf.PdfThumbnailProvider" />
|
||||
<RegistryValue Type="string" Name="DisplayName" Value="Pdf Thumbnail Provider" />
|
||||
<RegistryValue Type="string" Name="AppID" Value="{CF142243-F059-45AF-8842-DBBE9783DB14}" />
|
||||
<RegistryValue Type="string" Key="Implemented Categories\{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}" Value=""/>
|
||||
<RegistryValue Type="string" Key="InprocServer32" Value="[FileExplorerPreviewInstallFolder]PdfThumbnailProvider.comhost.dll" />
|
||||
<RegistryValue Type="string" Key="InprocServer32" Name="Assembly" Value="PdfThumbnailProvider, Version=$(var.Version).0, Culture=neutral" />
|
||||
<RegistryValue Type="string" Key="InprocServer32" Name="Class" Value="Microsoft.PowerToys.ThumbnailHandler.Pdf.PdfThumbnailProvider" />
|
||||
<RegistryValue Type="string" Key="InprocServer32" Name="ThreadingModel" Value="Both" />
|
||||
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="Assembly" Value="PdfThumbnailProvider, Version=$(var.Version).0, Culture=neutral" />
|
||||
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="Class" Value="Microsoft.PowerToys.ThumbnailHandler.Pdf.PdfThumbnailProvider" />
|
||||
</RegistryKey>
|
||||
<!-- Registry Key for AppID registration -->
|
||||
<RegistryKey Root="HKCR" Key="AppID\{CF142243-F059-45AF-8842-DBBE9783DB14}">
|
||||
<RegistryValue Type="expandable" Name="DllSurrogate" Value="%SystemRoot%\system32\prevhost.exe" />
|
||||
@@ -511,6 +557,10 @@
|
||||
<RegistryKey Root="HKLM" Key="Software\Microsoft\Windows\CurrentVersion\PreviewHandlers">
|
||||
<RegistryValue Type="string" Name="{45769bcc-e8fd-42d0-947e-02beef77a1f5}" Value="Markdown Preview Handler" />
|
||||
</RegistryKey>
|
||||
<!-- Add Pdf preview handler to preview handlers list -->
|
||||
<RegistryKey Root="HKLM" Key="Software\Microsoft\Windows\CurrentVersion\PreviewHandlers">
|
||||
<RegistryValue Type="string" Name="{07665729-6243-4746-95b7-79579308d1b2}" Value="Pdf Preview Handler" />
|
||||
</RegistryKey>
|
||||
<!-- Add file type association for Svg Preview Handler -->
|
||||
<RegistryKey Root="HKCR" Key=".svg\shellex">
|
||||
<RegistryValue Type="string" Key="{8895b1c6-b41f-4c1c-a562-0d564250836f}" Value="{ddee2b8a-6807-48a6-bb20-2338174ff779}" />
|
||||
@@ -523,6 +573,14 @@
|
||||
<RegistryKey Root="HKCR" Key=".md\shellex">
|
||||
<RegistryValue Type="string" Key="{8895b1c6-b41f-4c1c-a562-0d564250836f}" Value="{45769bcc-e8fd-42d0-947e-02beef77a1f5}" />
|
||||
</RegistryKey>
|
||||
<!-- Add file type association for Pdf Preview Handler -->
|
||||
<RegistryKey Root="HKCR" Key=".pdf\shellex">
|
||||
<RegistryValue Type="string" Key="{8895b1c6-b41f-4c1c-a562-0d564250836f}" Value="{07665729-6243-4746-95b7-79579308d1b2}" />
|
||||
</RegistryKey>
|
||||
<!-- Add file type association for Pdf Thumbnail Provider -->
|
||||
<RegistryKey Root="HKCR" Key=".pdf\shellex">
|
||||
<RegistryValue Type="string" Key="{E357FCCD-A995-4576-B01F-234630154E96}" Value="{BCC13D15-9720-4CC4-8371-EA74A274741E}" />
|
||||
</RegistryKey>
|
||||
<!-- Update Key to use IE11 for prevhost.exe -->
|
||||
<RegistryKey Root="HKLM" Key="Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION">
|
||||
<RegistryValue Type="integer" Name="prevhost.exe" Value="11000" />
|
||||
@@ -771,7 +829,7 @@
|
||||
<File Source="$(var.BinX64Dir)modules\FileExplorerPreview\SvgPreviewHandler.comhost.dll" />
|
||||
<File Source="$(var.BinX64Dir)modules\FileExplorerPreview\SvgPreviewHandler.runtimeconfig.json" />
|
||||
<File Source="$(var.BinX64Dir)modules\FileExplorerPreview\SvgPreviewHandler.deps.json" />
|
||||
<!-- File to include dll for Svg Preview Handler -->
|
||||
<!-- File to include dll for Svg Thumbnail Provider -->
|
||||
<File Source="$(var.BinX64Dir)modules\FileExplorerPreview\SvgThumbnailProvider.dll" />
|
||||
<File Source="$(var.BinX64Dir)modules\FileExplorerPreview\SvgThumbnailProvider.comhost.dll" />
|
||||
<File Source="$(var.BinX64Dir)modules\FileExplorerPreview\SvgThumbnailProvider.runtimeconfig.json" />
|
||||
@@ -784,6 +842,16 @@
|
||||
<File Source="$(var.BinX64Dir)modules\FileExplorerPreview\Markdig.Signed.dll" />
|
||||
<File Source="$(var.BinX64Dir)modules\FileExplorerPreview\HtmlAgilityPack.dll" />
|
||||
<File Id="FileExplorerPreview_System.IO.Abstractions.dll" Source="$(var.BinX64Dir)modules\FileExplorerPreview\System.IO.Abstractions.dll" />
|
||||
<!-- File to include dll for Pdf Preview Handler -->
|
||||
<File Source="$(var.BinX64Dir)modules\FileExplorerPreview\PdfPreviewHandler.dll" />
|
||||
<File Source="$(var.BinX64Dir)modules\FileExplorerPreview\PdfPreviewHandler.comhost.dll" />
|
||||
<File Source="$(var.BinX64Dir)modules\FileExplorerPreview\PdfPreviewHandler.runtimeconfig.json" />
|
||||
<File Source="$(var.BinX64Dir)modules\FileExplorerPreview\PdfPreviewHandler.deps.json" />
|
||||
<!-- File to include dll for Pdf Thumbnail Provider -->
|
||||
<File Source="$(var.BinX64Dir)modules\FileExplorerPreview\PdfThumbnailProvider.dll" />
|
||||
<File Source="$(var.BinX64Dir)modules\FileExplorerPreview\PdfThumbnailProvider.comhost.dll" />
|
||||
<File Source="$(var.BinX64Dir)modules\FileExplorerPreview\PdfThumbnailProvider.runtimeconfig.json" />
|
||||
<File Source="$(var.BinX64Dir)modules\FileExplorerPreview\PdfThumbnailProvider.deps.json" />
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
|
||||
@@ -853,10 +921,10 @@
|
||||
<?endforeach?>
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
<DirectoryRef Id="SettingsV2ControlsInstallFolder" FileSource="$(var.BinX64Dir)Settings\Controls">
|
||||
<DirectoryRef Id="SettingsV2ControlsInstallFolder" FileSource="$(var.BinX64Dir)Settings\Controls\ShortcutControl">
|
||||
<Component Id="SettingsV2Controls" Guid="05C55C88-B59A-4450-A07C-EB7626E0781A" Win64="yes">
|
||||
<?foreach File in HotkeySettingsControl.xbf?>
|
||||
<File Id="SettingsV2_Controls_$(var.File)" Source="$(var.BinX64Dir)Settings\Controls\$(var.File)" />
|
||||
<?foreach File in ShortcutControl.xbf?>
|
||||
<File Id="SettingsV2_Controls_$(var.File)" Source="$(var.BinX64Dir)Settings\Controls\ShortcutControl\$(var.File)" />
|
||||
<?endforeach?>
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
@@ -1022,7 +1090,10 @@
|
||||
<File Id="MarkdownPreviewHandler_$(var.IdSafeLanguage)_File" Source="$(var.BinX64Dir)modules\FileExplorerPreview\$(var.Language)\MarkdownPreviewHandler.resources.dll" />
|
||||
</Component>
|
||||
<Component Id="SVGPreviewHandler_$(var.IdSafeLanguage)_Component" Directory="Resource$(var.IdSafeLanguage)FileExplorerPreviewInstallFolder">
|
||||
<File Id="SVGPreviewHandler_$(var.IdSafeLanguage)_File" Source="$(var.BinX64Dir)modules\FileExplorerPreview\$(var.Language)\SvgPreviewHandler.resources.dll" />
|
||||
<File Id="SVGPreviewHandler_$(var.IdSafeLanguage)_File" Source="$(var.BinX64Dir)modules\FileExplorerPreview\$(var.Language)\SvgPreviewHandler.resources.dll" />
|
||||
</Component>
|
||||
<Component Id="PDFPreviewHandler_$(var.IdSafeLanguage)_Component" Directory="Resource$(var.IdSafeLanguage)FileExplorerPreviewInstallFolder">
|
||||
<File Id="PDFPreviewHandler_$(var.IdSafeLanguage)_File" Source="$(var.BinX64Dir)modules\FileExplorerPreview\$(var.Language)\PdfPreviewHandler.resources.dll" />
|
||||
</Component>
|
||||
<!-- PowerToys Run aka Launcher plugin resources -->
|
||||
<Component Id="Launcher_Calculator_$(var.IdSafeLanguage)_Component" Directory="Resource$(var.IdSafeLanguage)CalculatorPluginFolder">
|
||||
@@ -1058,9 +1129,9 @@
|
||||
<Component Id="Launcher_System_$(var.IdSafeLanguage)_Component" Directory="Resource$(var.IdSafeLanguage)SystemPluginFolder">
|
||||
<File Id="Launcher_System_$(var.IdSafeLanguage)_File" Source="$(var.BinX64Dir)modules\launcher\Plugins\System\$(var.Language)\Microsoft.PowerToys.Run.Plugin.System.resources.dll" />
|
||||
</Component>
|
||||
<!--<Component Id="Launcher_WindowsSettings_$(var.IdSafeLanguage)_Component" Directory="Resource$(var.IdSafeLanguage)WindowsSettingsPluginFolder">
|
||||
<Component Id="Launcher_WindowsSettings_$(var.IdSafeLanguage)_Component" Directory="Resource$(var.IdSafeLanguage)WindowsSettingsPluginFolder">
|
||||
<File Id="Launcher_WindowsSettings_$(var.IdSafeLanguage)_File" Source="$(var.BinX64Dir)modules\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.WindowsSettings\$(var.Language)\Microsoft.PowerToys.Run.Plugin.WindowsSettings.resources.dll" />
|
||||
</Component>-->
|
||||
</Component>
|
||||
<?undef IdSafeLanguage?>
|
||||
<?endforeach?>
|
||||
<?endif?>
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "resource.h"
|
||||
#include <ProjectTelemetry.h>
|
||||
|
||||
#include "../../src/common/utils/MsiUtils.h"
|
||||
#include "../../src/common/updating/installer.h"
|
||||
#include "../../src/common/version/version.h"
|
||||
|
||||
#include "../../installer/PowerToysBootstrapper/bootstrapper/RcResource.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
HINSTANCE DLL_HANDLE = nullptr;
|
||||
|
||||
TRACELOGGING_DEFINE_PROVIDER(
|
||||
g_hProvider,
|
||||
"Microsoft.PowerToysInstaller",
|
||||
@@ -21,6 +25,78 @@ const DWORD USERNAME_LEN = UNLEN + 1; // User Name + '\0'
|
||||
static const wchar_t* POWERTOYS_EXE_COMPONENT = L"{A2C66D91-3485-4D00-B04D-91844E6B345B}";
|
||||
static const wchar_t* POWERTOYS_UPGRADE_CODE = L"{42B84BF7-5FBF-473B-9C8B-049DC16F7708}";
|
||||
|
||||
UINT __stdcall InstallEmbeddedMSIXCA(MSIHANDLE hInstall)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
UINT er = ERROR_SUCCESS;
|
||||
hr = WcaInitialize(hInstall, "InstallEmbeddedMSIXCA");
|
||||
ExitOnFailure(hr, "Failed to initialize");
|
||||
|
||||
if (auto msix = RcResource::create(IDR_BIN_MSIX_HELLO_PACKAGE, L"BIN", DLL_HANDLE))
|
||||
{
|
||||
WcaLog(LOGMSG_STANDARD, "Extracted MSIX");
|
||||
// TODO: Use to activate embedded MSIX
|
||||
const auto msix_path = std::filesystem::temp_directory_path() / "hello_package.msix";
|
||||
if (!msix->saveAsFile(msix_path))
|
||||
{
|
||||
ExitOnFailure(hr, "Failed to save msix");
|
||||
}
|
||||
WcaLog(LOGMSG_STANDARD, "Saved MSIX");
|
||||
using namespace winrt::Windows::Management::Deployment;
|
||||
using namespace winrt::Windows::Foundation;
|
||||
|
||||
Uri msix_uri{ msix_path.wstring() };
|
||||
PackageManager pm;
|
||||
auto result = pm.AddPackageAsync(msix_uri, nullptr, DeploymentOptions::None).get();
|
||||
if (!result)
|
||||
{
|
||||
ExitOnFailure(hr, "Failed to AddPackage");
|
||||
}
|
||||
|
||||
WcaLog(LOGMSG_STANDARD, "MSIX[s] were installed!");
|
||||
}
|
||||
else
|
||||
{
|
||||
ExitOnFailure(hr, "Failed to extract msix");
|
||||
}
|
||||
|
||||
LExit:
|
||||
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
||||
return WcaFinalize(er);
|
||||
}
|
||||
|
||||
UINT __stdcall UninstallEmbeddedMSIXCA(MSIHANDLE hInstall)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
UINT er = ERROR_SUCCESS;
|
||||
using namespace winrt::Windows::Management::Deployment;
|
||||
using namespace winrt::Windows::Foundation;
|
||||
// TODO: This must be replaced with the actual publisher and package name
|
||||
const wchar_t package_name[] = L"46b35c25-b593-48d5-aeb1-d3e9c3b796e9";
|
||||
const wchar_t publisher[] = L"CN=yuyoyuppe";
|
||||
PackageManager pm;
|
||||
|
||||
hr = WcaInitialize(hInstall, "UninstallEmbeddedMSIXCA");
|
||||
ExitOnFailure(hr, "Failed to initialize");
|
||||
|
||||
for (const auto& p : pm.FindPackagesForUser({}, package_name, publisher))
|
||||
{
|
||||
auto result = pm.RemovePackageAsync(p.Id().FullName()).get();
|
||||
if (result)
|
||||
{
|
||||
WcaLog(LOGMSG_STANDARD, "MSIX was uninstalled!");
|
||||
}
|
||||
else
|
||||
{
|
||||
WcaLog(LOGMSG_STANDARD, "Couldn't uninstall MSIX!");
|
||||
}
|
||||
}
|
||||
|
||||
LExit:
|
||||
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
||||
return WcaFinalize(er);
|
||||
}
|
||||
|
||||
// Creates a Scheduled Task to run at logon for the current user.
|
||||
// The path of the executable to run should be passed as the CustomActionData (Value).
|
||||
// Based on the Task Scheduler Logon Trigger Example:
|
||||
@@ -42,12 +118,12 @@ UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall)
|
||||
ITaskSettings* pSettings = nullptr;
|
||||
ITriggerCollection* pTriggerCollection = nullptr;
|
||||
IRegisteredTask* pRegisteredTask = nullptr;
|
||||
IPrincipal * pPrincipal = nullptr;
|
||||
ITrigger * pTrigger = nullptr;
|
||||
ILogonTrigger * pLogonTrigger = nullptr;
|
||||
IAction * pAction = nullptr;
|
||||
IActionCollection * pActionCollection = nullptr;
|
||||
IExecAction * pExecAction = nullptr;
|
||||
IPrincipal* pPrincipal = nullptr;
|
||||
ITrigger* pTrigger = nullptr;
|
||||
ILogonTrigger* pLogonTrigger = nullptr;
|
||||
IAction* pAction = nullptr;
|
||||
IActionCollection* pActionCollection = nullptr;
|
||||
IExecAction* pExecAction = nullptr;
|
||||
|
||||
LPWSTR wszExecutablePath = nullptr;
|
||||
|
||||
@@ -190,7 +266,6 @@ UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall)
|
||||
pActionCollection->Release();
|
||||
ExitOnFailure(hr, "Cannot create the action: %x", hr);
|
||||
|
||||
|
||||
// QI for the executable task pointer.
|
||||
hr = pAction->QueryInterface(
|
||||
IID_IExecAction, (void**)&pExecAction);
|
||||
@@ -302,7 +377,7 @@ UINT __stdcall RemoveScheduledTasksCA(MSIHANDLE hInstall)
|
||||
ITaskService* pService = nullptr;
|
||||
ITaskFolder* pTaskFolder = nullptr;
|
||||
IRegisteredTaskCollection* pTaskCollection = nullptr;
|
||||
ITaskFolder * pRootFolder = nullptr;
|
||||
ITaskFolder* pRootFolder = nullptr;
|
||||
LONG numTasks = 0;
|
||||
|
||||
hr = WcaInitialize(hInstall, "RemoveScheduledTasksCA");
|
||||
@@ -589,9 +664,8 @@ UINT __stdcall DetectPrevInstallPathCA(MSIHANDLE hInstall)
|
||||
MsiSetPropertyW(hInstall, L"INSTALLFOLDER", install_path->data());
|
||||
}
|
||||
}
|
||||
catch(...)
|
||||
catch (...)
|
||||
{
|
||||
|
||||
}
|
||||
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
||||
return WcaFinalize(er);
|
||||
@@ -609,7 +683,7 @@ UINT __stdcall CertifyVirtualCameraDriverCA(MSIHANDLE hInstall)
|
||||
HCERTSTORE hCertStore = nullptr;
|
||||
HANDLE hfile = nullptr;
|
||||
DWORD size = INVALID_FILE_SIZE;
|
||||
char * pFileContent = nullptr;
|
||||
char* pFileContent = nullptr;
|
||||
|
||||
hr = WcaInitialize(hInstall, "CertifyVirtualCameraDriverCA");
|
||||
ExitOnFailure(hr, "Failed to initialize", hr);
|
||||
@@ -648,11 +722,11 @@ UINT __stdcall CertifyVirtualCameraDriverCA(MSIHANDLE hInstall)
|
||||
}
|
||||
|
||||
if (!CertAddEncodedCertificateToStore(hCertStore,
|
||||
X509_ASN_ENCODING,
|
||||
(const BYTE*)pFileContent,
|
||||
size,
|
||||
CERT_STORE_ADD_ALWAYS,
|
||||
nullptr))
|
||||
X509_ASN_ENCODING,
|
||||
(const BYTE*)pFileContent,
|
||||
size,
|
||||
CERT_STORE_ADD_ALWAYS,
|
||||
nullptr))
|
||||
{
|
||||
hr = GetLastError();
|
||||
ExitOnFailure(hr, "Adding certificate failed", hr);
|
||||
@@ -678,13 +752,11 @@ LExit:
|
||||
MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_WARNING + MB_OK), hRecord);
|
||||
}
|
||||
|
||||
|
||||
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
||||
return WcaFinalize(er);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
UINT __stdcall InstallVirtualCameraDriverCA(MSIHANDLE hInstall)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
@@ -793,7 +865,7 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
|
||||
}
|
||||
wchar_t processName[MAX_PATH] = L"<unknown>";
|
||||
|
||||
HANDLE hProcess{OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE, FALSE, procID)};
|
||||
HANDLE hProcess{ OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE, FALSE, procID) };
|
||||
if (!hProcess)
|
||||
{
|
||||
continue;
|
||||
@@ -819,7 +891,7 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
|
||||
GetWindowThreadProcessId(hwnd, &windowProcID);
|
||||
if (windowProcID == targetProcID)
|
||||
{
|
||||
DWORD_PTR _ {};
|
||||
DWORD_PTR _{};
|
||||
SendMessageTimeoutA(hwnd, WM_CLOSE, 0, 0, SMTO_BLOCK, timeout, &_);
|
||||
}
|
||||
return TRUE;
|
||||
@@ -837,7 +909,6 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
|
||||
return WcaFinalize(er);
|
||||
}
|
||||
|
||||
|
||||
// DllMain - Initialize and cleanup WiX custom action utils.
|
||||
extern "C" BOOL WINAPI DllMain(__in HINSTANCE hInst, __in ULONG ulReason, __in LPVOID)
|
||||
{
|
||||
@@ -846,6 +917,7 @@ extern "C" BOOL WINAPI DllMain(__in HINSTANCE hInst, __in ULONG ulReason, __in L
|
||||
case DLL_PROCESS_ATTACH:
|
||||
WcaGlobalInitialize(hInst);
|
||||
TraceLoggingRegister(g_hProvider);
|
||||
DLL_HANDLE = hInst;
|
||||
break;
|
||||
|
||||
case DLL_PROCESS_DETACH:
|
||||
|
||||
@@ -16,4 +16,6 @@ EXPORTS
|
||||
TerminateProcessesCA
|
||||
CertifyVirtualCameraDriverCA
|
||||
InstallVirtualCameraDriverCA
|
||||
InstallEmbeddedMSIXCA
|
||||
UninstallVirtualCameraDriverCA
|
||||
UninstallEmbeddedMSIXCA
|
||||
@@ -15,7 +15,7 @@
|
||||
<ProjectGuid>{32f3882b-f2d6-4586-b5ed-11e39e522bd3}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>PowerToysSetupCustomActions</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
@@ -53,10 +53,11 @@
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>inc;..\..\src\;telemetry;$(WIX)sdk\$(WixPlatformToolset)\inc;$(SolutionDir)\packages\WiX.3.11.2\tools\sdk\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalOptions>/await /Zc:twoPhase- /Wv:18 %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalLibraryDirectories>$(WIX)sdk\$(WixPlatformToolset)\lib\x64;$(SolutionDir)\packages\WiX.3.11.2\tools\sdk\vs2017\lib\x64;..\..\$(PlatformShortName)\$(Configuration)\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>Newdev.lib;Crypt32.lib;msi.lib;wcautil.lib;Psapi.lib;Pathcch.lib;comsupp.lib;taskschd.lib;Secur32.lib;msi.lib;dutil.lib;wcautil.lib;Version.lib;ApplicationUpdate.lib;Notifications.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>WindowsApp.lib;Newdev.lib;Crypt32.lib;msi.lib;wcautil.lib;Psapi.lib;Pathcch.lib;comsupp.lib;taskschd.lib;Secur32.lib;msi.lib;dutil.lib;wcautil.lib;Version.lib;ApplicationUpdate.lib;Notifications.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
@@ -112,10 +113,15 @@
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\PowerToysBootstrapper\bootstrapper\RcResource.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="stdafx.h" />
|
||||
<ClInclude Include="Telemetry\ProjectTelemetry.h" />
|
||||
<ClInclude Include="Telemetry\TraceLoggingDefines.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="Resource.rc" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
@@ -124,4 +130,4 @@
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\WiX.3.11.2\build\wix.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\WiX.3.11.2\build\wix.props'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -12,6 +12,8 @@
|
||||
<ClInclude Include="Telemetry\TraceLoggingDefines.h">
|
||||
<Filter>Telemetry</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="..\PowerToysBootstrapper\bootstrapper\RcResource.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="CustomAction.def" />
|
||||
@@ -22,4 +24,7 @@
|
||||
<UniqueIdentifier>{6e73ce5d-e715-4e7e-b796-c5d180b07ff2}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="Resource.rc" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
61
installer/PowerToysSetupCustomActions/Resource.rc
Normal file
61
installer/PowerToysSetupCustomActions/Resource.rc
Normal file
@@ -0,0 +1,61 @@
|
||||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#include "resource.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "winres.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (United States) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS)
|
||||
LANGUAGE 25, 1
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE
|
||||
BEGIN
|
||||
"#include ""winres.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE
|
||||
BEGIN
|
||||
"\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
#endif // English (United States) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// TODO: Use to activate embedded MSIX
|
||||
//IDR_BIN_MSIX_HELLO_PACKAGE BIN "..\\..\..\\src\\modules\\HelloModule\\AppPackages\\HelloModule_1.0.0.0_x64_Test\\HelloModule_1.0.0.0_x64.msix"
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
16
installer/PowerToysSetupCustomActions/resource.h
Normal file
16
installer/PowerToysSetupCustomActions/resource.h
Normal file
@@ -0,0 +1,16 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by Resource.rc
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 102
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1001
|
||||
#define _APS_NEXT_SYMED_VALUE 102
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define IDR_BIN_MSIX_HELLO_PACKAGE 101
|
||||
@@ -27,3 +27,9 @@
|
||||
#include <psapi.h>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
#include <winrt/Windows.ApplicationModel.h>
|
||||
#include <winrt/Windows.Management.Deployment.h>
|
||||
#include <winrt/Windows.System.h>
|
||||
|
||||
@@ -35,12 +35,12 @@
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.200902.2" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.210204.1" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -95,10 +95,10 @@
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\modules\videoconference\VideoConferenceShared\MicrophoneDevice.cpp">
|
||||
<CompileAsManaged>false</CompileAsManaged>
|
||||
</ClCompile>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\modules\videoconference\VideoConferenceShared\VideoCaptureDeviceList.cpp">
|
||||
<CompileAsManaged>false</CompileAsManaged>
|
||||
</ClCompile>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Generated Files\AssemblyInfo.cpp" />
|
||||
<ClCompile Include="HotkeyManager.cpp" />
|
||||
<ClCompile Include="interop.cpp" />
|
||||
@@ -121,10 +121,15 @@
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="ExtensionTargets" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -32,8 +32,8 @@
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\modules\videoconference\VideoConferenceShared\VideoCaptureDeviceList.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="shared_constants.h">
|
||||
</ClInclude>
|
||||
<ClInclude Include="shared_constants.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
@@ -68,4 +68,7 @@
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.200902.2" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.210204.1" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -42,7 +42,7 @@
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
@@ -50,6 +50,6 @@
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.200729.8" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.200902.2" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.210204.1" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.200729.8" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.200902.2" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.210204.1" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -62,7 +62,7 @@
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
@@ -70,6 +70,6 @@
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -24,9 +24,6 @@
|
||||
<ClInclude Include="installer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="winstore.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="updateState.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
||||
@@ -39,7 +39,6 @@ inline std::wstring get_process_path(HWND window) noexcept
|
||||
{
|
||||
// It is a UWP app. We will enumerate the windows and look for one created
|
||||
// by something with a different PID
|
||||
// It might take a time to connect the process. That's the reason for the retry loop here
|
||||
DWORD new_pid = pid;
|
||||
|
||||
EnumChildWindows(
|
||||
@@ -103,4 +102,4 @@ inline std::wstring get_module_folderpath(HMODULE mod = nullptr, const bool remo
|
||||
PathRemoveFileSpecW(buffer);
|
||||
}
|
||||
return { buffer, (UINT)lstrlenW(buffer) };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ public:
|
||||
PowerToysSettings::PowerToyValues values =
|
||||
PowerToysSettings::PowerToyValues::from_json_string(config, get_key());
|
||||
|
||||
ParseHotkey(values);
|
||||
ParseSettings(values);
|
||||
}
|
||||
catch (std::exception ex)
|
||||
{
|
||||
@@ -119,6 +119,10 @@ public:
|
||||
virtual std::optional<HotkeyEx> GetHotkeyEx() override
|
||||
{
|
||||
Logger::trace("GetHotkeyEx()");
|
||||
if (m_shouldReactToPressedWinKey)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
return m_hotkey;
|
||||
}
|
||||
|
||||
@@ -154,6 +158,16 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool keep_track_of_pressed_win_key() override
|
||||
{
|
||||
return m_shouldReactToPressedWinKey;
|
||||
}
|
||||
|
||||
virtual UINT milliseconds_win_key_must_be_pressed() override
|
||||
{
|
||||
return m_millisecondsWinKeyShouldBePressed;
|
||||
}
|
||||
|
||||
private:
|
||||
std::wstring app_name;
|
||||
//contains the non localized key of the powertoy
|
||||
@@ -163,6 +177,12 @@ private:
|
||||
|
||||
// Hotkey to invoke the module
|
||||
HotkeyEx m_hotkey;
|
||||
|
||||
// If the module should be activated through the legacy pressing windows key behavior.
|
||||
const UINT DEFAULT_MILLISECONDS_WIN_KEY_SHOULD_BE_PRESSED = 900;
|
||||
bool m_shouldReactToPressedWinKey = false;
|
||||
UINT m_millisecondsWinKeyShouldBePressed = DEFAULT_MILLISECONDS_WIN_KEY_SHOULD_BE_PRESSED;
|
||||
|
||||
HANDLE exitEvent;
|
||||
|
||||
bool StartProcess(std::wstring args = L"")
|
||||
@@ -239,7 +259,7 @@ private:
|
||||
PowerToysSettings::PowerToyValues settings =
|
||||
PowerToysSettings::PowerToyValues::load_from_settings_file(app_key);
|
||||
|
||||
ParseHotkey(settings);
|
||||
ParseSettings(settings);
|
||||
}
|
||||
catch (std::exception ex)
|
||||
{
|
||||
@@ -251,13 +271,17 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void ParseHotkey(PowerToysSettings::PowerToyValues& settings)
|
||||
void ParseSettings(PowerToysSettings::PowerToyValues& settings)
|
||||
{
|
||||
m_shouldReactToPressedWinKey = false;
|
||||
m_millisecondsWinKeyShouldBePressed = DEFAULT_MILLISECONDS_WIN_KEY_SHOULD_BE_PRESSED;
|
||||
|
||||
auto settingsObject = settings.get_raw_json();
|
||||
if (settingsObject.GetView().Size())
|
||||
{
|
||||
try
|
||||
{
|
||||
// Parse HotKey
|
||||
auto jsonHotkeyObject = settingsObject.GetNamedObject(L"properties").GetNamedObject(L"open_shortcutguide");
|
||||
auto hotkey = PowerToysSettings::HotkeyObject::from_json(jsonHotkeyObject);
|
||||
m_hotkey = HotkeyEx();
|
||||
@@ -287,6 +311,18 @@ private:
|
||||
{
|
||||
Logger::warn("Failed to initialize Shortcut Guide start shortcut");
|
||||
}
|
||||
try
|
||||
{
|
||||
// Parse Legacy windows key press behavior settings
|
||||
auto jsonUseLegacyWinKeyBehaviorObject = settingsObject.GetNamedObject(L"properties").GetNamedObject(L"use_legacy_press_win_key_behavior");
|
||||
m_shouldReactToPressedWinKey = (bool)jsonUseLegacyWinKeyBehaviorObject.GetNamedBoolean(L"value");
|
||||
auto jsonPressTimeObject = settingsObject.GetNamedObject(L"properties").GetNamedObject(L"press_time");
|
||||
m_millisecondsWinKeyShouldBePressed = (UINT)jsonPressTimeObject.GetNamedNumber(L"value");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Logger::warn("Failed to get legacy win key behavior settings");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
BorderThickness="1"
|
||||
Title="Color Picker"
|
||||
Height="380"
|
||||
Width="400"
|
||||
Width="440"
|
||||
ResizeMode="NoResize"
|
||||
Topmost="True"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
<Border x:Name="MainBorder"
|
||||
Margin="12,16,12,0"
|
||||
Width="308"
|
||||
Width="348"
|
||||
Height="36"
|
||||
CornerRadius="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
@@ -20,7 +20,7 @@
|
||||
<ColumnDefinition Width="36"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock x:Name="FormatNameTextBlock"
|
||||
Opacity="0.4"
|
||||
Opacity="0.6"
|
||||
Foreground="{DynamicResource PrimaryForegroundBrush}"
|
||||
Margin="8"
|
||||
FontWeight="SemiBold"
|
||||
@@ -44,7 +44,8 @@
|
||||
<Button x:Name="CopyToClipboardButton"
|
||||
ToolTipService.ToolTip="{x:Static p:Resources.Copy_to_clipboard}"
|
||||
Background="{DynamicResource ColorControlBackgroundBrush}"
|
||||
Foreground="{DynamicResource SecondaryForegroundBrush}"
|
||||
Foreground="{DynamicResource PrimaryForegroundBrush}"
|
||||
Opacity="0.6"
|
||||
Height="36"
|
||||
Width="36"
|
||||
Grid.Column="2"
|
||||
|
||||
@@ -239,11 +239,11 @@
|
||||
ShadowDepth="2" />
|
||||
</Grid.Effect>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="36" />
|
||||
<ColumnDefinition Width="36" />
|
||||
<ColumnDefinition Width="46" />
|
||||
<ColumnDefinition Width="46" />
|
||||
<ColumnDefinition Width="165" />
|
||||
<ColumnDefinition Width="36" />
|
||||
<ColumnDefinition Width="36" />
|
||||
<ColumnDefinition Width="46" />
|
||||
<ColumnDefinition Width="46" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button x:Name="colorVariation1Button"
|
||||
@@ -288,7 +288,7 @@
|
||||
Background="Red"
|
||||
Width="165"
|
||||
Height="48"
|
||||
Margin="72,0,0,0"
|
||||
Margin="92,0,0,0"
|
||||
AutomationProperties.Name="{x:Static p:Resources.Selected_color}"
|
||||
AutomationProperties.HelpText="{x:Static p:Resources.Selected_color_helptext}"
|
||||
ToolTipService.ToolTip="{x:Static p:Resources.Selected_color_tooltip}"
|
||||
@@ -398,7 +398,7 @@
|
||||
Width="72"
|
||||
ui:ControlHelper.CornerRadius="2,0,0,2"
|
||||
AutomationProperties.Name="{x:Static p:Resources.Red_value}"
|
||||
ValueChanged="RGBNumberBox_ValueChanged"
|
||||
TextBoxBase.TextChanged="RGBNumberBox_TextChanged"
|
||||
Minimum="0"
|
||||
Maximum="255" />
|
||||
|
||||
@@ -408,7 +408,7 @@
|
||||
Width="72"
|
||||
ui:ControlHelper.CornerRadius="0"
|
||||
AutomationProperties.Name="{x:Static p:Resources.Green_value}"
|
||||
ValueChanged="RGBNumberBox_ValueChanged"
|
||||
TextBoxBase.TextChanged="RGBNumberBox_TextChanged"
|
||||
Minimum="0"
|
||||
Maximum="255" />
|
||||
|
||||
@@ -418,7 +418,7 @@
|
||||
Margin="-1,0,0,0"
|
||||
ui:ControlHelper.CornerRadius="0,2,2,0"
|
||||
AutomationProperties.Name="{x:Static p:Resources.Blue_value}"
|
||||
ValueChanged="RGBNumberBox_ValueChanged"
|
||||
TextBoxBase.TextChanged="RGBNumberBox_TextChanged"
|
||||
Minimum="0"
|
||||
Maximum="255" />
|
||||
</StackPanel>
|
||||
|
||||
@@ -12,6 +12,7 @@ using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
using ColorPicker.Helpers;
|
||||
using ModernWpf.Controls;
|
||||
using ModernWpf.Controls.Primitives;
|
||||
|
||||
namespace ColorPicker.Controls
|
||||
@@ -190,13 +191,7 @@ namespace ColorPicker.Controls
|
||||
{
|
||||
_isCollapsed = false;
|
||||
|
||||
var opacityAppear = new DoubleAnimation(1.0, new Duration(TimeSpan.FromMilliseconds(300)));
|
||||
opacityAppear.EasingFunction = new QuadraticEase() { EasingMode = EasingMode.EaseInOut };
|
||||
|
||||
var resize = new DoubleAnimation(400, new Duration(TimeSpan.FromMilliseconds(300)));
|
||||
resize.EasingFunction = new ExponentialEase() { EasingMode = EasingMode.EaseInOut };
|
||||
|
||||
var resizeColor = new DoubleAnimation(309, new Duration(TimeSpan.FromMilliseconds(250)));
|
||||
var resizeColor = new DoubleAnimation(349, new Duration(TimeSpan.FromMilliseconds(250)));
|
||||
resizeColor.EasingFunction = new ExponentialEase() { EasingMode = EasingMode.EaseInOut };
|
||||
|
||||
var moveColor = new ThicknessAnimation(new Thickness(0), new Duration(TimeSpan.FromMilliseconds(250)));
|
||||
@@ -216,16 +211,10 @@ namespace ColorPicker.Controls
|
||||
{
|
||||
_isCollapsed = true;
|
||||
|
||||
var opacityAppear = new DoubleAnimation(0, new Duration(TimeSpan.FromMilliseconds(150)));
|
||||
opacityAppear.EasingFunction = new QuadraticEase() { EasingMode = EasingMode.EaseInOut };
|
||||
|
||||
var resize = new DoubleAnimation(0, new Duration(TimeSpan.FromMilliseconds(150)));
|
||||
resize.EasingFunction = new ExponentialEase() { EasingMode = EasingMode.EaseInOut };
|
||||
|
||||
var resizeColor = new DoubleAnimation(165, new Duration(TimeSpan.FromMilliseconds(150)));
|
||||
resizeColor.EasingFunction = new ExponentialEase() { EasingMode = EasingMode.EaseInOut };
|
||||
|
||||
var moveColor = new ThicknessAnimation(new Thickness(72, 0, 0, 0), new Duration(TimeSpan.FromMilliseconds(150)));
|
||||
var moveColor = new ThicknessAnimation(new Thickness(92, 0, 0, 0), new Duration(TimeSpan.FromMilliseconds(150)));
|
||||
moveColor.EasingFunction = new ExponentialEase() { EasingMode = EasingMode.EaseInOut };
|
||||
|
||||
ControlHelper.SetCornerRadius(CurrentColorButton, new CornerRadius(0));
|
||||
@@ -297,22 +286,6 @@ namespace ColorPicker.Controls
|
||||
_ignoreGradientsChanges = false;
|
||||
}
|
||||
|
||||
private static Point GetMousePositionWithinGrid(Border border)
|
||||
{
|
||||
var pos = System.Windows.Input.Mouse.GetPosition(border);
|
||||
if (pos.X < 0)
|
||||
{
|
||||
pos.X = 0;
|
||||
}
|
||||
|
||||
if (pos.X > border.Width)
|
||||
{
|
||||
pos.X = border.Width;
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
private void HexCode_TextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
var newValue = (sender as TextBox).Text;
|
||||
@@ -336,21 +309,6 @@ namespace ColorPicker.Controls
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable CA1801 // Review unused parameters
|
||||
private void RGBNumberBox_ValueChanged(ModernWpf.Controls.NumberBox sender, ModernWpf.Controls.NumberBoxValueChangedEventArgs args)
|
||||
#pragma warning restore CA1801 // Review unused parameters
|
||||
{
|
||||
if (!_ignoreRGBChanges)
|
||||
{
|
||||
var r = byte.Parse(RNumberBox.Text, CultureInfo.InvariantCulture);
|
||||
var g = byte.Parse(GNumberBox.Text, CultureInfo.InvariantCulture);
|
||||
var b = byte.Parse(BNumberBox.Text, CultureInfo.InvariantCulture);
|
||||
_ignoreRGBChanges = true;
|
||||
SetColorFromTextBoxes(System.Drawing.Color.FromArgb(r, g, b));
|
||||
_ignoreRGBChanges = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetColorFromTextBoxes(System.Drawing.Color color)
|
||||
{
|
||||
if (!_ignoreGradientsChanges)
|
||||
@@ -377,6 +335,65 @@ namespace ColorPicker.Controls
|
||||
{
|
||||
(sender as System.Windows.Controls.TextBox).SelectAll();
|
||||
}
|
||||
|
||||
private void RGBNumberBox_TextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
if (!_ignoreRGBChanges)
|
||||
{
|
||||
var numberBox = sender as NumberBox;
|
||||
var r = numberBox.Name == "RNumberBox" ? GetValueFromNumberBox(numberBox) : (byte)RNumberBox.Value;
|
||||
var g = numberBox.Name == "GNumberBox" ? GetValueFromNumberBox(numberBox) : (byte)GNumberBox.Value;
|
||||
var b = numberBox.Name == "BNumberBox" ? GetValueFromNumberBox(numberBox) : (byte)BNumberBox.Value;
|
||||
_ignoreRGBChanges = true;
|
||||
SetColorFromTextBoxes(System.Drawing.Color.FromArgb(r, g, b));
|
||||
_ignoreRGBChanges = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NumberBox provides value only after it has been validated - happens after pressing enter or leaving this control.
|
||||
/// However, we need to get value immediately after the underlying textbox value changes
|
||||
/// </summary>
|
||||
/// <param name="numberBox">numberBox control which value we want to get</param>
|
||||
/// <returns>Validated value as per numberbox conditions, if content is invalid it returns previous value</returns>
|
||||
private static byte GetValueFromNumberBox(NumberBox numberBox)
|
||||
{
|
||||
var internalTextBox = GetChildOfType<TextBox>(numberBox);
|
||||
var parsedValue = numberBox.NumberFormatter.ParseDouble(internalTextBox.Text);
|
||||
if (parsedValue != null)
|
||||
{
|
||||
var parsedValueByte = (byte)parsedValue;
|
||||
if (parsedValueByte >= numberBox.Minimum && parsedValueByte <= numberBox.Maximum)
|
||||
{
|
||||
return parsedValueByte;
|
||||
}
|
||||
}
|
||||
|
||||
// not valid input, return previous value
|
||||
return (byte)numberBox.Value;
|
||||
}
|
||||
|
||||
public static T GetChildOfType<T>(DependencyObject depObj)
|
||||
where T : DependencyObject
|
||||
{
|
||||
if (depObj == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
|
||||
{
|
||||
var child = VisualTreeHelper.GetChild(depObj, i);
|
||||
|
||||
var result = (child as T) ?? GetChildOfType<T>(child);
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable SA1402 // File may only contain a single type
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace ColorPicker.Helpers
|
||||
internal static class ColorHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert a given <see cref="Color"/> to a CYMK color (cyan, magenta, yellow, black key)
|
||||
/// Convert a given <see cref="Color"/> to a CMYK color (cyan, magenta, yellow, black key)
|
||||
/// </summary>
|
||||
/// <param name="color">The <see cref="Color"/> to convert</param>
|
||||
/// <returns>The cyan[0..1], magenta[0..1], yellow[0..1] and black key[0..1] of the converted color</returns>
|
||||
@@ -139,6 +139,77 @@ namespace ColorPicker.Helpers
|
||||
return (GetNaturalColorFromHue(color.GetHue()), min, 1 - max);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a given <see cref="Color"/> to a CIE LAB color (LAB)
|
||||
/// </summary>
|
||||
/// <param name="color">The <see cref="Color"/> to convert</param>
|
||||
/// <returns>The lightness [0..100] and two chromaticities [-128..127]</returns>
|
||||
internal static (double lightness, double chromaticityA, double chromaticityB) ConvertToCIELABColor(Color color)
|
||||
{
|
||||
var xyz = ConvertToCIEXYZColor(color);
|
||||
var lab = GetCIELABColorFromCIEXYZ(xyz.x, xyz.y, xyz.z);
|
||||
|
||||
return lab;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a given <see cref="Color"/> to a CIE XYZ color (XYZ)
|
||||
/// The constants of the formula used come from this wikipedia page:
|
||||
/// https://en.wikipedia.org/wiki/SRGB#The_reverse_transformation_(sRGB_to_CIE_XYZ)
|
||||
/// </summary>
|
||||
/// <param name="color">The <see cref="Color"/> to convert</param>
|
||||
/// <returns>The X [0..1], Y [0..1] and Z [0..1]</returns>
|
||||
internal static (double x, double y, double z) ConvertToCIEXYZColor(Color color)
|
||||
{
|
||||
double r = color.R / 255d;
|
||||
double g = color.G / 255d;
|
||||
double b = color.B / 255d;
|
||||
|
||||
// inverse companding, gamma correction must be undone
|
||||
double rLinear = (r > 0.04045) ? Math.Pow((r + 0.055) / 1.055, 2.4) : (r / 12.92);
|
||||
double gLinear = (g > 0.04045) ? Math.Pow((g + 0.055) / 1.055, 2.4) : (g / 12.92);
|
||||
double bLinear = (b > 0.04045) ? Math.Pow((b + 0.055) / 1.055, 2.4) : (b / 12.92);
|
||||
|
||||
return (
|
||||
(rLinear * 0.4124) + (gLinear * 0.3576) + (bLinear * 0.1805),
|
||||
(rLinear * 0.2126) + (gLinear * 0.7152) + (bLinear * 0.0722),
|
||||
(rLinear * 0.0193) + (gLinear * 0.1192) + (bLinear * 0.9505)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a CIE XYZ color <see cref="double"/> to a CIE LAB color (LAB)
|
||||
/// The constants of the formula used come from this wikipedia page:
|
||||
/// https://en.wikipedia.org/wiki/CIELAB_color_space#Converting_between_CIELAB_and_CIEXYZ_coordinates
|
||||
/// </summary>
|
||||
/// <param name="x">The <see cref="x"/> represents a mix of the three CIE RGB curves</param>
|
||||
/// <param name="y">The <see cref="y"/> represents the luminance</param>
|
||||
/// <param name="z">The <see cref="z"/> is quasi-equal to blue (of CIE RGB)</param>
|
||||
/// <returns>The lightness [0..100] and two chromaticities [-128..127]</returns>
|
||||
private static (double lightness, double chromaticityA, double chromaticityB)
|
||||
GetCIELABColorFromCIEXYZ(double x, double y, double z)
|
||||
{
|
||||
// These values are based on the D65 Illuminant
|
||||
x = x * 100 / 95.0489;
|
||||
y = y * 100 / 100.0;
|
||||
z = z * 100 / 108.8840;
|
||||
|
||||
// XYZ to CIELab transformation
|
||||
double delta = 6d / 29;
|
||||
double m = (1d / 3) * Math.Pow(delta, -2);
|
||||
double t = Math.Pow(delta, 3);
|
||||
|
||||
double fx = (x > t) ? Math.Pow(x, 1.0 / 3.0) : (x * m) + (16.0 / 116.0);
|
||||
double fy = (y > t) ? Math.Pow(y, 1.0 / 3.0) : (y * m) + (16.0 / 116.0);
|
||||
double fz = (z > t) ? Math.Pow(z, 1.0 / 3.0) : (z * m) + (16.0 / 116.0);
|
||||
|
||||
double l = (116 * fy) - 16;
|
||||
double a = 500 * (fx - fy);
|
||||
double b = 200 * (fy - fz);
|
||||
|
||||
return (l, a, b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the natural color for the given hue value
|
||||
/// </summary>
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace ColorPicker.Helpers
|
||||
internal static string GetStringRepresentation(Color color, ColorRepresentationType colorRepresentationType)
|
||||
=> colorRepresentationType switch
|
||||
{
|
||||
ColorRepresentationType.CMYK => ColorToCYMK(color),
|
||||
ColorRepresentationType.CMYK => ColorToCMYK(color),
|
||||
ColorRepresentationType.HEX => ColorToHex(color),
|
||||
ColorRepresentationType.HSB => ColorToHSB(color),
|
||||
ColorRepresentationType.HSI => ColorToHSI(color),
|
||||
@@ -44,17 +44,19 @@ namespace ColorPicker.Helpers
|
||||
ColorRepresentationType.HWB => ColorToHWB(color),
|
||||
ColorRepresentationType.NCol => ColorToNCol(color),
|
||||
ColorRepresentationType.RGB => ColorToRGB(color),
|
||||
ColorRepresentationType.CIELAB => ColorToCIELAB(color),
|
||||
ColorRepresentationType.CIEXYZ => ColorToCIEXYZ(color),
|
||||
|
||||
// Fall-back value, when "_userSettings.CopiedColorRepresentation.Value" is incorrect
|
||||
_ => ColorToHex(color),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Return a <see cref="string"/> representation of a CYMK color
|
||||
/// Return a <see cref="string"/> representation of a CMYK color
|
||||
/// </summary>
|
||||
/// <param name="color">The <see cref="Color"/> for the CYMK color presentation</param>
|
||||
/// <returns>A <see cref="string"/> representation of a CYMK color</returns>
|
||||
private static string ColorToCYMK(Color color)
|
||||
/// <param name="color">The <see cref="Color"/> for the CMYK color presentation</param>
|
||||
/// <returns>A <see cref="string"/> representation of a CMYK color</returns>
|
||||
private static string ColorToCMYK(Color color)
|
||||
{
|
||||
var (cyan, magenta, yellow, blackKey) = ColorHelper.ConvertToCMYKColor(color);
|
||||
|
||||
@@ -191,11 +193,46 @@ namespace ColorPicker.Helpers
|
||||
/// <summary>
|
||||
/// Return a <see cref="string"/> representation of a RGB color
|
||||
/// </summary>
|
||||
/// <param name="color">The see cref="Color"/> for the RGB color presentation</param>
|
||||
/// <param name="color">The <see cref="Color"/> for the RGB color presentation</param>
|
||||
/// <returns>A <see cref="string"/> representation of a RGB color</returns>
|
||||
private static string ColorToRGB(Color color)
|
||||
=> $"rgb({color.R.ToString(CultureInfo.InvariantCulture)}"
|
||||
+ $", {color.G.ToString(CultureInfo.InvariantCulture)}"
|
||||
+ $", {color.B.ToString(CultureInfo.InvariantCulture)})";
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="string"/> representation of a CIE LAB color
|
||||
/// </summary>
|
||||
/// <param name="color">The <see cref="Color"/> for the CIE LAB color presentation</param>
|
||||
/// <returns>A <see cref="string"/> representation of a CIE LAB color</returns>
|
||||
private static string ColorToCIELAB(Color color)
|
||||
{
|
||||
var (lightness, chromaticityA, chromaticityB) = ColorHelper.ConvertToCIELABColor(color);
|
||||
lightness = Math.Round(lightness, 2);
|
||||
chromaticityA = Math.Round(chromaticityA, 2);
|
||||
chromaticityB = Math.Round(chromaticityB, 2);
|
||||
|
||||
return $"CIELab({lightness.ToString(CultureInfo.InvariantCulture)}" +
|
||||
$", {chromaticityA.ToString(CultureInfo.InvariantCulture)}" +
|
||||
$", {chromaticityB.ToString(CultureInfo.InvariantCulture)})";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="string"/> representation of a CIE XYZ color
|
||||
/// </summary>
|
||||
/// <param name="color">The <see cref="Color"/> for the CIE XYZ color presentation</param>
|
||||
/// <returns>A <see cref="string"/> representation of a CIE XYZ color</returns>
|
||||
private static string ColorToCIEXYZ(Color color)
|
||||
{
|
||||
var (x, y, z) = ColorHelper.ConvertToCIEXYZColor(color);
|
||||
|
||||
x = Math.Round(x * 100, 4);
|
||||
y = Math.Round(y * 100, 4);
|
||||
z = Math.Round(z * 100, 4);
|
||||
|
||||
return $"xyz({x.ToString(CultureInfo.InvariantCulture)}" +
|
||||
$", {y.ToString(CultureInfo.InvariantCulture)}" +
|
||||
$", {z.ToString(CultureInfo.InvariantCulture)})";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,6 +197,18 @@ namespace ColorPicker.ViewModels
|
||||
FormatName = ColorRepresentationType.NCol.ToString(),
|
||||
Convert = (Color color) => { return ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.NCol); },
|
||||
});
|
||||
_allColorRepresentations.Add(
|
||||
new ColorFormatModel()
|
||||
{
|
||||
FormatName = ColorRepresentationType.CIELAB.ToString(),
|
||||
Convert = (Color color) => { return ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.CIELAB); },
|
||||
});
|
||||
_allColorRepresentations.Add(
|
||||
new ColorFormatModel()
|
||||
{
|
||||
FormatName = ColorRepresentationType.CIEXYZ.ToString(),
|
||||
Convert = (Color color) => { return ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.CIEXYZ); },
|
||||
});
|
||||
|
||||
_userSettings.VisibleColorFormats.CollectionChanged += VisibleColorFormats_CollectionChanged;
|
||||
|
||||
|
||||
@@ -304,6 +304,119 @@ namespace Microsoft.ColorPicker.UnitTests
|
||||
Assert.AreEqual(result.blackness * 100d, blackness, 0.5d);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DataRow("FFFFFF", 100.00, 0.00, -0.01)] // white
|
||||
[DataRow("808080", 53.59, 0.00, -0.01)] // gray
|
||||
[DataRow("000000", 0.00, 0.00, 0.00)] // black
|
||||
[DataRow("FF0000", 53.23, 80.11, 67.22)] // red
|
||||
[DataRow("008000", 46.23, -51.70, 49.90)] // green
|
||||
[DataRow("80FFFF", 93.16, -35.23, -10.87)] // cyan
|
||||
[DataRow("8080FF", 59.20, 33.1, -63.47)] // blue
|
||||
[DataRow("BF40BF", 50.10, 65.51, -41.49)] // magenta
|
||||
[DataRow("BFBF00", 75.04, -17.35, 76.03)] // yellow
|
||||
[DataRow("008000", 46.23, -51.70, 49.90)] // green
|
||||
[DataRow("8080FF", 59.20, 33.1, -63.47)] // blue
|
||||
[DataRow("BF40BF", 50.10, 65.51, -41.49)] // magenta
|
||||
[DataRow("0048BA", 34.35, 27.94, -64.81)] // absolute zero
|
||||
[DataRow("B0BF1A", 73.91, -23.39, 71.15)] // acid green
|
||||
[DataRow("D0FF14", 93.87, -40.21, 88.97)] // arctic lime
|
||||
[DataRow("1B4D3E", 29.13, -20.97, 3.95)] // brunswick green
|
||||
[DataRow("FFEF00", 93.01, -13.86, 91.48)] // canary yellow
|
||||
[DataRow("FFA600", 75.16, 23.41, 79.11)] // cheese
|
||||
[DataRow("1A2421", 13.18, -5.23, 0.56)] // dark jungle green
|
||||
[DataRow("003399", 25.77, 28.89, -59.10)] // dark powder blue
|
||||
[DataRow("D70A53", 46.03, 71.91, 18.02)] // debian red
|
||||
[DataRow("80FFD5", 92.09, -45.08, 9.28)] // fathom secret green
|
||||
[DataRow("EFDFBB", 89.26, -0.13, 19.64)] // dutch white
|
||||
[DataRow("5218FA", 36.65, 75.63, -97.71)] // han purple
|
||||
[DataRow("FF496C", 59.07, 69.90, 21.79)] // infra red
|
||||
[DataRow("545AA7", 41.20, 19.32, -42.35)] // liberty
|
||||
[DataRow("E6A8D7", 75.91, 30.13, -14.80)] // light orchid
|
||||
[DataRow("ADDFAD", 84.32, -25.67, 19.36)] // light moss green
|
||||
[DataRow("E3F988", 94.25, -23.70, 51.57)] // mindaro
|
||||
public void ColorRGBtoCIELABTest(string hexValue, double lightness, double chromaticityA, double chromaticityB)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(hexValue))
|
||||
{
|
||||
Assert.IsNotNull(hexValue);
|
||||
}
|
||||
|
||||
Assert.IsTrue(hexValue.Length >= 6);
|
||||
|
||||
var red = int.Parse(hexValue.Substring(0, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
|
||||
var green = int.Parse(hexValue.Substring(2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
|
||||
var blue = int.Parse(hexValue.Substring(4, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
|
||||
|
||||
var color = Color.FromArgb(255, red, green, blue);
|
||||
var result = ColorHelper.ConvertToCIELABColor(color);
|
||||
|
||||
// lightness[0..100]
|
||||
Assert.AreEqual(Math.Round(result.lightness, 2), lightness);
|
||||
|
||||
// chromaticityA[-128..127]
|
||||
Assert.AreEqual(Math.Round(result.chromaticityA, 2), chromaticityA);
|
||||
|
||||
// chromaticityB[-128..127]
|
||||
Assert.AreEqual(Math.Round(result.chromaticityB, 2), chromaticityB);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DataRow("FFFFFF", 95.0500, 100.0000, 108.9000)] // white
|
||||
[DataRow("808080", 20.5175, 21.5861, 23.5072)] // gray
|
||||
[DataRow("000000", 0.0000, 0.0000, 0.0000)] // black
|
||||
[DataRow("FF0000", 41.2400, 21.2600, 1.9300)] // red
|
||||
[DataRow("008000", 7.7192, 15.4383, 2.5731)] // green
|
||||
[DataRow("80FFFF", 62.7121, 83.3292, 107.3866)] // cyan
|
||||
[DataRow("8080FF", 34.6713, 27.2475, 98.0397)] // blue
|
||||
[DataRow("BF40BF", 32.7232, 18.5047, 51.1373)] // magenta
|
||||
[DataRow("BFBF00", 40.1167, 48.3380, 7.2158)] // yellow
|
||||
[DataRow("008000", 7.7192, 15.4383, 2.5731)] // green
|
||||
[DataRow("80FFFF", 62.7121, 83.3292, 107.3866)] // cyan
|
||||
[DataRow("8080FF", 34.6713, 27.2475, 98.0397)] // blue
|
||||
[DataRow("BF40BF", 32.7232, 18.5047, 51.1373)] // magenta
|
||||
[DataRow("0048BA", 11.1803, 8.1799, 47.4440)] // absolute zero
|
||||
[DataRow("B0BF1A", 36.7218, 46.5663, 8.0300)] // acid green
|
||||
[DataRow("D0FF14", 61.8987, 84.9804, 13.8023)] // arctic lime
|
||||
[DataRow("1B4D3E", 3.9754, 5.8886, 5.4845)] // brunswick green
|
||||
[DataRow("FFEF00", 72.1065, 82.9930, 12.2188)] // canary yellow
|
||||
[DataRow("FFA600", 54.8762, 48.5324, 6.4754)] // cheese
|
||||
[DataRow("1A2421", 1.3314, 1.5912, 1.6758)] // dark jungle green
|
||||
[DataRow("003399", 6.9336, 4.6676, 30.6725)] // dark powder blue
|
||||
[DataRow("D70A53", 29.6942, 15.2887, 9.5696)] // debian red
|
||||
[DataRow("80FFD5", 56.6723, 80.9133, 75.5817)] // fathom secret green
|
||||
[DataRow("EFDFBB", 70.9539, 74.7139, 57.6953)] // dutch white
|
||||
[DataRow("5218FA", 21.0616, 9.3492, 91.1370)] // han purple
|
||||
[DataRow("FF496C", 46.3293, 27.1078, 16.9779)] // infra red
|
||||
[DataRow("545AA7", 14.2874, 11.9872, 38.1199)] // liberty
|
||||
[DataRow("E6A8D7", 58.9015, 49.7346, 70.7853)] // light orchid
|
||||
[DataRow("ADDFAD", 51.1641, 64.6767, 49.3224)] // light moss green
|
||||
[DataRow("E3F988", 69.9982, 85.8598, 36.1759)] // mindaro
|
||||
public void ColorRGBtoCIEXYZTest(string hexValue, double x, double y, double z)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(hexValue))
|
||||
{
|
||||
Assert.IsNotNull(hexValue);
|
||||
}
|
||||
|
||||
Assert.IsTrue(hexValue.Length >= 6);
|
||||
|
||||
var red = int.Parse(hexValue.Substring(0, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
|
||||
var green = int.Parse(hexValue.Substring(2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
|
||||
var blue = int.Parse(hexValue.Substring(4, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
|
||||
|
||||
var color = Color.FromArgb(255, red, green, blue);
|
||||
var result = ColorHelper.ConvertToCIEXYZColor(color);
|
||||
|
||||
// x[0..0.95047]
|
||||
Assert.AreEqual(Math.Round(result.x * 100, 4), x);
|
||||
|
||||
// y[0..1]
|
||||
Assert.AreEqual(Math.Round(result.y * 100, 4), y);
|
||||
|
||||
// z[0..1.08883]
|
||||
Assert.AreEqual(Math.Round(result.z * 100, 4), z);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ColorRGBtoCMYKZeroDivTest()
|
||||
{
|
||||
|
||||
@@ -22,6 +22,8 @@ namespace Microsoft.ColorPicker.UnitTests
|
||||
[DataRow(ColorRepresentationType.HSV, "hsv(0, 0%, 0%)")]
|
||||
[DataRow(ColorRepresentationType.HWB, "hwb(0, 0%, 100%)")]
|
||||
[DataRow(ColorRepresentationType.RGB, "rgb(0, 0, 0)")]
|
||||
[DataRow(ColorRepresentationType.CIELAB, "CIELab(0, 0, 0)")]
|
||||
[DataRow(ColorRepresentationType.CIEXYZ, "xyz(0, 0, 0)")]
|
||||
|
||||
public void GetStringRepresentationTest(ColorRepresentationType type, string expected)
|
||||
{
|
||||
|
||||
@@ -241,6 +241,8 @@ FancyZones::Run() noexcept
|
||||
PostMessage(m_window, WM_HOTKEY, 1, 0);
|
||||
}
|
||||
});
|
||||
|
||||
FancyZonesDataInstance().SetVirtualDesktopCheckCallback(std::bind(&VirtualDesktop::IsVirtualDesktopIdSavedInRegistry, &m_virtualDesktop, std::placeholders::_1));
|
||||
}
|
||||
|
||||
// IFancyZones
|
||||
@@ -356,7 +358,8 @@ void FancyZones::WindowCreated(HWND window) noexcept
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_virtualDesktop.IsWindowOnCurrentDesktop(window))
|
||||
auto desktopId = m_virtualDesktop.GetDesktopId(window);
|
||||
if (desktopId.has_value() && *desktopId != m_currentDesktopId)
|
||||
{
|
||||
// Switch between virtual desktops results with posting same windows messages that also indicate
|
||||
// creation of new window. We need to check if window being processed is on currently active desktop.
|
||||
@@ -682,7 +685,7 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa
|
||||
}
|
||||
else if (message == WM_PRIV_VD_UPDATE)
|
||||
{
|
||||
RegisterVirtualDesktopUpdates();
|
||||
OnDisplayChange(DisplayChangeType::Initialization);
|
||||
}
|
||||
else if (message == WM_PRIV_EDITOR)
|
||||
{
|
||||
@@ -754,7 +757,14 @@ void FancyZones::OnDisplayChange(DisplayChangeType changeType) noexcept
|
||||
changeType == DisplayChangeType::Initialization)
|
||||
{
|
||||
m_previousDesktopId = m_currentDesktopId;
|
||||
auto currentVirtualDesktopId = m_virtualDesktop.GetCurrentVirtualDesktopId();
|
||||
|
||||
auto currentVirtualDesktopId = m_virtualDesktop.GetCurrentVirtualDesktopIdFromRegistry();
|
||||
if (!currentVirtualDesktopId.has_value())
|
||||
{
|
||||
Logger::info("Virtual Desktop id from top level window");
|
||||
currentVirtualDesktopId = m_virtualDesktop.GetDesktopIdByTopLevelWindows();
|
||||
}
|
||||
|
||||
if (currentVirtualDesktopId.has_value())
|
||||
{
|
||||
m_currentDesktopId = *currentVirtualDesktopId;
|
||||
@@ -791,33 +801,49 @@ void FancyZones::AddZoneWindow(HMONITOR monitor, const std::wstring& deviceId) n
|
||||
_TRACER_;
|
||||
if (m_workAreaHandler.IsNewWorkArea(m_currentDesktopId, monitor))
|
||||
{
|
||||
wil::unique_cotaskmem_string virtualDesktopId;
|
||||
if (SUCCEEDED(StringFromCLSID(m_currentDesktopId, &virtualDesktopId)))
|
||||
wil::unique_cotaskmem_string virtualDesktopIdStr;
|
||||
if (!SUCCEEDED(StringFromCLSID(m_currentDesktopId, &virtualDesktopIdStr)))
|
||||
{
|
||||
std::wstring uniqueId;
|
||||
Logger::debug(L"Add new work area on virtual desktop {}", virtualDesktopIdStr.get());
|
||||
}
|
||||
|
||||
FancyZonesDataTypes::DeviceIdData uniqueId;
|
||||
uniqueId.virtualDesktopId = m_currentDesktopId;
|
||||
|
||||
if (monitor)
|
||||
{
|
||||
uniqueId = FancyZonesUtils::GenerateUniqueId(monitor, deviceId, virtualDesktopId.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
uniqueId = FancyZonesUtils::GenerateUniqueIdAllMonitorsArea(virtualDesktopId.get());
|
||||
}
|
||||
if (monitor)
|
||||
{
|
||||
uniqueId.deviceName = FancyZonesUtils::TrimDeviceId(deviceId);
|
||||
|
||||
std::wstring parentId{};
|
||||
auto parentArea = m_workAreaHandler.GetWorkArea(m_previousDesktopId, monitor);
|
||||
if (parentArea)
|
||||
MONITORINFOEXW mi;
|
||||
mi.cbSize = sizeof(mi);
|
||||
if (GetMonitorInfo(monitor, &mi))
|
||||
{
|
||||
parentId = parentArea->UniqueId();
|
||||
const FancyZonesUtils::Rect monitorRect(mi.rcMonitor);
|
||||
uniqueId.width = monitorRect.width();
|
||||
uniqueId.height = monitorRect.height();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uniqueId.deviceName = ZonedWindowProperties::MultiMonitorDeviceID;
|
||||
|
||||
RECT combinedResolution = FancyZonesUtils::GetAllMonitorsCombinedRect<&MONITORINFO::rcMonitor>();
|
||||
uniqueId.width = combinedResolution.right - combinedResolution.left;
|
||||
uniqueId.height = combinedResolution.bottom - combinedResolution.top;
|
||||
}
|
||||
|
||||
auto workArea = MakeWorkArea(m_hinstance, monitor, uniqueId, parentId, GetZoneColors(), m_settings->GetSettings()->overlappingZonesAlgorithm);
|
||||
if (workArea)
|
||||
{
|
||||
m_workAreaHandler.AddWorkArea(m_currentDesktopId, monitor, workArea);
|
||||
FancyZonesDataInstance().SaveZoneSettings();
|
||||
}
|
||||
FancyZonesDataTypes::DeviceIdData parentId{};
|
||||
auto parentArea = m_workAreaHandler.GetWorkArea(m_previousDesktopId, monitor);
|
||||
if (parentArea)
|
||||
{
|
||||
parentId = parentArea->UniqueId();
|
||||
}
|
||||
|
||||
auto workArea = MakeWorkArea(m_hinstance, monitor, uniqueId, parentId, GetZoneColors(), m_settings->GetSettings()->overlappingZonesAlgorithm);
|
||||
if (workArea)
|
||||
{
|
||||
m_workAreaHandler.AddWorkArea(m_currentDesktopId, monitor, workArea);
|
||||
FancyZonesDataInstance().SaveZoneSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -838,34 +864,34 @@ LRESULT CALLBACK FancyZones::s_WndProc(HWND window, UINT message, WPARAM wparam,
|
||||
|
||||
void FancyZones::UpdateZoneWindows() noexcept
|
||||
{
|
||||
// Mapping between display device name and device index (operating system identifies each display device with an index value).
|
||||
std::unordered_map<std::wstring, DWORD> displayDeviceIdxMap;
|
||||
struct capture
|
||||
{
|
||||
FancyZones* fancyZones;
|
||||
std::unordered_map<std::wstring, DWORD>* displayDeviceIdx;
|
||||
};
|
||||
|
||||
auto callback = [](HMONITOR monitor, HDC, RECT*, LPARAM data) -> BOOL {
|
||||
capture* params = reinterpret_cast<capture*>(data);
|
||||
MONITORINFOEX mi{ { .cbSize = sizeof(mi) } };
|
||||
if (GetMonitorInfoW(monitor, &mi))
|
||||
{
|
||||
auto& displayDeviceIdxMap = *(params->displayDeviceIdx);
|
||||
FancyZones* fancyZones = params->fancyZones;
|
||||
|
||||
std::wstring deviceId = FancyZonesUtils::GetDisplayDeviceId(mi.szDevice, displayDeviceIdxMap);
|
||||
fancyZones->AddZoneWindow(monitor, deviceId);
|
||||
}
|
||||
return TRUE;
|
||||
};
|
||||
|
||||
if (m_settings->GetSettings()->spanZonesAcrossMonitors)
|
||||
{
|
||||
AddZoneWindow(nullptr, {});
|
||||
}
|
||||
else
|
||||
{
|
||||
// Mapping between display device name and device index (operating system identifies each display device with an index value).
|
||||
std::unordered_map<std::wstring, DWORD> displayDeviceIdxMap;
|
||||
struct capture
|
||||
{
|
||||
FancyZones* fancyZones;
|
||||
std::unordered_map<std::wstring, DWORD>* displayDeviceIdx;
|
||||
};
|
||||
|
||||
auto callback = [](HMONITOR monitor, HDC, RECT*, LPARAM data) -> BOOL {
|
||||
capture* params = reinterpret_cast<capture*>(data);
|
||||
MONITORINFOEX mi{ { .cbSize = sizeof(mi) } };
|
||||
if (GetMonitorInfoW(monitor, &mi))
|
||||
{
|
||||
auto& displayDeviceIdxMap = *(params->displayDeviceIdx);
|
||||
FancyZones* fancyZones = params->fancyZones;
|
||||
|
||||
std::wstring deviceId = FancyZonesUtils::GetDisplayDeviceId(mi.szDevice, displayDeviceIdxMap);
|
||||
fancyZones->AddZoneWindow(monitor, deviceId);
|
||||
}
|
||||
return TRUE;
|
||||
};
|
||||
|
||||
capture capture{ this, &displayDeviceIdxMap };
|
||||
EnumDisplayMonitors(nullptr, nullptr, callback, reinterpret_cast<LPARAM>(&capture));
|
||||
}
|
||||
@@ -873,22 +899,15 @@ void FancyZones::UpdateZoneWindows() noexcept
|
||||
|
||||
void FancyZones::UpdateWindowsPositions() noexcept
|
||||
{
|
||||
auto callback = [](HWND window, LPARAM data) -> BOOL {
|
||||
for (const auto [window, desktopId] : m_virtualDesktop.GetWindowsRelatedToDesktops())
|
||||
{
|
||||
auto zoneIndexSet = GetZoneIndexSet(window);
|
||||
auto strongThis = reinterpret_cast<FancyZones*>(data);
|
||||
auto desktopId = strongThis->m_virtualDesktop.GetWindowDesktopId(window);
|
||||
if (desktopId.has_value())
|
||||
auto zoneWindow = m_workAreaHandler.GetWorkArea(window, desktopId);
|
||||
if (zoneWindow)
|
||||
{
|
||||
auto zoneWindow = strongThis->m_workAreaHandler.GetWorkArea(window, *desktopId);
|
||||
if (zoneWindow)
|
||||
{
|
||||
strongThis->m_windowMoveHandler.MoveWindowIntoZoneByIndexSet(window, zoneIndexSet, zoneWindow);
|
||||
}
|
||||
m_windowMoveHandler.MoveWindowIntoZoneByIndexSet(window, zoneIndexSet, zoneWindow);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
};
|
||||
EnumWindows(callback, reinterpret_cast<LPARAM>(this));
|
||||
}
|
||||
}
|
||||
|
||||
bool FancyZones::OnSnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode) noexcept
|
||||
@@ -1106,28 +1125,14 @@ void FancyZones::RegisterVirtualDesktopUpdates() noexcept
|
||||
{
|
||||
_TRACER_;
|
||||
|
||||
auto guids = m_virtualDesktop.GetVirtualDesktopIds();
|
||||
std::vector<std::wstring> guidStrings{};
|
||||
auto guids = m_virtualDesktop.GetVirtualDesktopIdsFromRegistry();
|
||||
if (guids.has_value())
|
||||
{
|
||||
m_workAreaHandler.RegisterUpdates(*guids);
|
||||
|
||||
for (auto& guid : *guids)
|
||||
{
|
||||
auto guidString = FancyZonesUtils::GuidToString(guid);
|
||||
if (guidString.has_value())
|
||||
{
|
||||
guidStrings.push_back(*guidString);
|
||||
}
|
||||
}
|
||||
|
||||
if (!guidStrings.empty())
|
||||
{
|
||||
FancyZonesDataInstance().UpdatePrimaryDesktopData(guidStrings[0]);
|
||||
}
|
||||
|
||||
FancyZonesDataInstance().RemoveDeletedDesktops(guidStrings);
|
||||
FancyZonesDataInstance().RemoveDeletedDesktops(*guids);
|
||||
}
|
||||
|
||||
FancyZonesDataInstance().SyncVirtualDesktops(m_currentDesktopId);
|
||||
}
|
||||
|
||||
void FancyZones::OnSettingsChanged() noexcept
|
||||
@@ -1162,6 +1167,7 @@ void FancyZones::OnEditorExitEvent() noexcept
|
||||
{
|
||||
// Collect information about changes in zone layout after editor exited.
|
||||
FancyZonesDataInstance().LoadFancyZonesData();
|
||||
FancyZonesDataInstance().SyncVirtualDesktops(m_currentDesktopId);
|
||||
UpdateZoneSets();
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "ZoneSet.h"
|
||||
#include "Settings.h"
|
||||
#include "CallTracer.h"
|
||||
#include "GuidUtils.h"
|
||||
|
||||
#include <common/Display/dpi_aware.h>
|
||||
#include <common/utils/json.h>
|
||||
@@ -30,7 +31,6 @@ namespace NonLocalizable
|
||||
const wchar_t FancyZonesDataFile[] = L"zones-settings.json";
|
||||
const wchar_t FancyZonesAppZoneHistoryFile[] = L"app-zone-history.json";
|
||||
const wchar_t FancyZonesEditorParametersFile[] = L"editor-parameters.json";
|
||||
const wchar_t DefaultGuid[] = L"{00000000-0000-0000-0000-000000000000}";
|
||||
const wchar_t RegistryPath[] = L"Software\\SuperFancyZones";
|
||||
}
|
||||
|
||||
@@ -157,6 +157,11 @@ FancyZonesData::FancyZonesData()
|
||||
editorParametersFileName = saveFolderPath + L"\\" + std::wstring(NonLocalizable::FancyZonesEditorParametersFile);
|
||||
}
|
||||
|
||||
void FancyZonesData::SetVirtualDesktopCheckCallback(std::function<bool(GUID)> callback)
|
||||
{
|
||||
m_virtualDesktopCheckCallback = callback;
|
||||
}
|
||||
|
||||
const JSONHelpers::TDeviceInfoMap& FancyZonesData::GetDeviceInfoMap() const
|
||||
{
|
||||
std::scoped_lock lock{ dataLock };
|
||||
@@ -175,7 +180,7 @@ const std::unordered_map<std::wstring, std::vector<FancyZonesDataTypes::AppZoneH
|
||||
return appZoneHistoryMap;
|
||||
}
|
||||
|
||||
std::optional<FancyZonesDataTypes::DeviceInfoData> FancyZonesData::FindDeviceInfo(const std::wstring& zoneWindowId) const
|
||||
std::optional<FancyZonesDataTypes::DeviceInfoData> FancyZonesData::FindDeviceInfo(const FancyZonesDataTypes::DeviceIdData& zoneWindowId) const
|
||||
{
|
||||
std::scoped_lock lock{ dataLock };
|
||||
auto it = deviceInfoMap.find(zoneWindowId);
|
||||
@@ -189,7 +194,7 @@ std::optional<FancyZonesDataTypes::CustomZoneSetData> FancyZonesData::FindCustom
|
||||
return it != end(customZoneSetsMap) ? std::optional{ it->second } : std::nullopt;
|
||||
}
|
||||
|
||||
bool FancyZonesData::AddDevice(const std::wstring& deviceId)
|
||||
bool FancyZonesData::AddDevice(const FancyZonesDataTypes::DeviceIdData& deviceId)
|
||||
{
|
||||
_TRACER_;
|
||||
using namespace FancyZonesDataTypes;
|
||||
@@ -197,6 +202,12 @@ bool FancyZonesData::AddDevice(const std::wstring& deviceId)
|
||||
std::scoped_lock lock{ dataLock };
|
||||
if (!deviceInfoMap.contains(deviceId))
|
||||
{
|
||||
wil::unique_cotaskmem_string virtualDesktopId;
|
||||
if (SUCCEEDED(StringFromCLSID(deviceId.virtualDesktopId, &virtualDesktopId)))
|
||||
{
|
||||
Logger::info(L"Create new device on virtual desktop {}", virtualDesktopId.get());
|
||||
}
|
||||
|
||||
// Creates default entry in map when WorkArea is created
|
||||
GUID guid;
|
||||
auto result{ CoCreateGuid(&guid) };
|
||||
@@ -218,7 +229,7 @@ bool FancyZonesData::AddDevice(const std::wstring& deviceId)
|
||||
return false;
|
||||
}
|
||||
|
||||
void FancyZonesData::CloneDeviceInfo(const std::wstring& source, const std::wstring& destination)
|
||||
void FancyZonesData::CloneDeviceInfo(const FancyZonesDataTypes::DeviceIdData& source, const FancyZonesDataTypes::DeviceIdData& destination)
|
||||
{
|
||||
if (source == destination)
|
||||
{
|
||||
@@ -235,7 +246,7 @@ void FancyZonesData::CloneDeviceInfo(const std::wstring& source, const std::wstr
|
||||
deviceInfoMap[destination] = deviceInfoMap[source];
|
||||
}
|
||||
|
||||
void FancyZonesData::UpdatePrimaryDesktopData(const std::wstring& desktopId)
|
||||
void FancyZonesData::SyncVirtualDesktops(GUID currentVirtualDesktopId)
|
||||
{
|
||||
_TRACER_;
|
||||
// Explorer persists current virtual desktop identifier to registry on a per session basis,
|
||||
@@ -244,9 +255,6 @@ void FancyZonesData::UpdatePrimaryDesktopData(const std::wstring& desktopId)
|
||||
// that case (00000000-0000-0000-0000-000000000000).
|
||||
// This method will go through all our persisted data with default GUID and update it with
|
||||
// valid one.
|
||||
auto replaceDesktopId = [&desktopId](const std::wstring& deviceId) {
|
||||
return deviceId.substr(0, deviceId.rfind('_') + 1) + desktopId;
|
||||
};
|
||||
|
||||
std::scoped_lock lock{ dataLock };
|
||||
bool dirtyFlag = false;
|
||||
@@ -255,53 +263,89 @@ void FancyZonesData::UpdatePrimaryDesktopData(const std::wstring& desktopId)
|
||||
{
|
||||
for (auto& data : perDesktopData)
|
||||
{
|
||||
if (ExtractVirtualDesktopId(data.deviceId) == NonLocalizable::DefaultGuid)
|
||||
if (data.deviceId.virtualDesktopId == GUID_NULL)
|
||||
{
|
||||
data.deviceId = replaceDesktopId(data.deviceId);
|
||||
data.deviceId.virtualDesktopId = currentVirtualDesktopId;
|
||||
dirtyFlag = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_virtualDesktopCheckCallback && !m_virtualDesktopCheckCallback(data.deviceId.virtualDesktopId))
|
||||
{
|
||||
data.deviceId.virtualDesktopId = GUID_NULL;
|
||||
dirtyFlag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<FancyZonesDataTypes::DeviceIdData> replaceWithCurrentId{};
|
||||
std::vector<FancyZonesDataTypes::DeviceIdData> replaceWithNullId{};
|
||||
|
||||
for (const auto& [desktopId, data] : deviceInfoMap)
|
||||
{
|
||||
if (desktopId.virtualDesktopId == GUID_NULL)
|
||||
{
|
||||
replaceWithCurrentId.push_back(desktopId);
|
||||
dirtyFlag = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_virtualDesktopCheckCallback && !m_virtualDesktopCheckCallback(desktopId.virtualDesktopId))
|
||||
{
|
||||
replaceWithNullId.push_back(desktopId);
|
||||
dirtyFlag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::wstring> toReplace{};
|
||||
|
||||
for (const auto& [id, data] : deviceInfoMap)
|
||||
{
|
||||
if (ExtractVirtualDesktopId(id) == NonLocalizable::DefaultGuid)
|
||||
{
|
||||
toReplace.push_back(id);
|
||||
dirtyFlag = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& id : toReplace)
|
||||
for (const auto& id : replaceWithCurrentId)
|
||||
{
|
||||
auto mapEntry = deviceInfoMap.extract(id);
|
||||
mapEntry.key() = replaceDesktopId(id);
|
||||
mapEntry.key().virtualDesktopId = currentVirtualDesktopId;
|
||||
deviceInfoMap.insert(std::move(mapEntry));
|
||||
}
|
||||
|
||||
for (const auto& id : replaceWithNullId)
|
||||
{
|
||||
auto mapEntry = deviceInfoMap.extract(id);
|
||||
mapEntry.key().virtualDesktopId = GUID_NULL;
|
||||
deviceInfoMap.insert(std::move(mapEntry));
|
||||
}
|
||||
|
||||
// TODO: when updating the primary desktop GUID, the app zone history also needs to be updated
|
||||
if (dirtyFlag)
|
||||
{
|
||||
SaveZoneSettings();
|
||||
wil::unique_cotaskmem_string virtualDesktopIdStr;
|
||||
if (SUCCEEDED(StringFromCLSID(currentVirtualDesktopId, &virtualDesktopIdStr)))
|
||||
{
|
||||
Logger::info(L"Update Virtual Desktop id to {}", virtualDesktopIdStr.get());
|
||||
}
|
||||
|
||||
SaveAppZoneHistoryAndZoneSettings();
|
||||
}
|
||||
}
|
||||
|
||||
void FancyZonesData::RemoveDeletedDesktops(const std::vector<std::wstring>& activeDesktops)
|
||||
void FancyZonesData::RemoveDeletedDesktops(const std::vector<GUID>& activeDesktops)
|
||||
{
|
||||
std::unordered_set<std::wstring> active(std::begin(activeDesktops), std::end(activeDesktops));
|
||||
std::unordered_set<GUID> active(std::begin(activeDesktops), std::end(activeDesktops));
|
||||
std::scoped_lock lock{ dataLock };
|
||||
bool dirtyFlag = false;
|
||||
|
||||
for (auto it = std::begin(deviceInfoMap); it != std::end(deviceInfoMap);)
|
||||
{
|
||||
std::wstring desktopId = ExtractVirtualDesktopId(it->first);
|
||||
if (desktopId != NonLocalizable::DefaultGuid)
|
||||
GUID desktopId = it->first.virtualDesktopId;
|
||||
|
||||
if (desktopId != GUID_NULL)
|
||||
{
|
||||
auto foundId = active.find(desktopId);
|
||||
if (foundId == std::end(active))
|
||||
{
|
||||
wil::unique_cotaskmem_string virtualDesktopIdStr;
|
||||
if (SUCCEEDED(StringFromCLSID(desktopId, &virtualDesktopIdStr)))
|
||||
{
|
||||
Logger::info(L"Remove Virtual Desktop id {}", virtualDesktopIdStr.get());
|
||||
}
|
||||
|
||||
RemoveDesktopAppZoneHistory(desktopId);
|
||||
it = deviceInfoMap.erase(it);
|
||||
dirtyFlag = true;
|
||||
@@ -317,7 +361,7 @@ void FancyZonesData::RemoveDeletedDesktops(const std::vector<std::wstring>& acti
|
||||
}
|
||||
}
|
||||
|
||||
bool FancyZonesData::IsAnotherWindowOfApplicationInstanceZoned(HWND window, const std::wstring_view& deviceId) const
|
||||
bool FancyZonesData::IsAnotherWindowOfApplicationInstanceZoned(HWND window, const FancyZonesDataTypes::DeviceIdData& deviceId) const
|
||||
{
|
||||
std::scoped_lock lock{ dataLock };
|
||||
auto processPath = get_process_path(window);
|
||||
@@ -329,7 +373,7 @@ bool FancyZonesData::IsAnotherWindowOfApplicationInstanceZoned(HWND window, cons
|
||||
auto& perDesktopData = history->second;
|
||||
for (auto& data : perDesktopData)
|
||||
{
|
||||
if (data.deviceId == deviceId)
|
||||
if (data.deviceId.isEqualWithNullVirtualDesktopId(deviceId))
|
||||
{
|
||||
DWORD processId = 0;
|
||||
GetWindowThreadProcessId(window, &processId);
|
||||
@@ -352,7 +396,7 @@ bool FancyZonesData::IsAnotherWindowOfApplicationInstanceZoned(HWND window, cons
|
||||
return false;
|
||||
}
|
||||
|
||||
void FancyZonesData::UpdateProcessIdToHandleMap(HWND window, const std::wstring_view& deviceId)
|
||||
void FancyZonesData::UpdateProcessIdToHandleMap(HWND window, const FancyZonesDataTypes::DeviceIdData& deviceId)
|
||||
{
|
||||
std::scoped_lock lock{ dataLock };
|
||||
auto processPath = get_process_path(window);
|
||||
@@ -364,7 +408,7 @@ void FancyZonesData::UpdateProcessIdToHandleMap(HWND window, const std::wstring_
|
||||
auto& perDesktopData = history->second;
|
||||
for (auto& data : perDesktopData)
|
||||
{
|
||||
if (data.deviceId == deviceId)
|
||||
if (data.deviceId.isEqualWithNullVirtualDesktopId(deviceId))
|
||||
{
|
||||
DWORD processId = 0;
|
||||
GetWindowThreadProcessId(window, &processId);
|
||||
@@ -376,7 +420,7 @@ void FancyZonesData::UpdateProcessIdToHandleMap(HWND window, const std::wstring_
|
||||
}
|
||||
}
|
||||
|
||||
ZoneIndexSet FancyZonesData::GetAppLastZoneIndexSet(HWND window, const std::wstring_view& deviceId, const std::wstring_view& zoneSetId) const
|
||||
ZoneIndexSet FancyZonesData::GetAppLastZoneIndexSet(HWND window, const FancyZonesDataTypes::DeviceIdData& deviceId, const std::wstring_view& zoneSetId) const
|
||||
{
|
||||
std::scoped_lock lock{ dataLock };
|
||||
auto processPath = get_process_path(window);
|
||||
@@ -388,7 +432,7 @@ ZoneIndexSet FancyZonesData::GetAppLastZoneIndexSet(HWND window, const std::wstr
|
||||
const auto& perDesktopData = history->second;
|
||||
for (const auto& data : perDesktopData)
|
||||
{
|
||||
if (data.zoneSetUuid == zoneSetId && data.deviceId == deviceId)
|
||||
if (data.zoneSetUuid == zoneSetId && data.deviceId.isEqualWithNullVirtualDesktopId(deviceId))
|
||||
{
|
||||
return data.zoneIndexSet;
|
||||
}
|
||||
@@ -399,7 +443,7 @@ ZoneIndexSet FancyZonesData::GetAppLastZoneIndexSet(HWND window, const std::wstr
|
||||
return {};
|
||||
}
|
||||
|
||||
bool FancyZonesData::RemoveAppLastZone(HWND window, const std::wstring_view& deviceId, const std::wstring_view& zoneSetId)
|
||||
bool FancyZonesData::RemoveAppLastZone(HWND window, const FancyZonesDataTypes::DeviceIdData& deviceId, const std::wstring_view& zoneSetId)
|
||||
{
|
||||
_TRACER_;
|
||||
std::scoped_lock lock{ dataLock };
|
||||
@@ -412,7 +456,7 @@ bool FancyZonesData::RemoveAppLastZone(HWND window, const std::wstring_view& dev
|
||||
auto& perDesktopData = history->second;
|
||||
for (auto data = std::begin(perDesktopData); data != std::end(perDesktopData);)
|
||||
{
|
||||
if (data->deviceId == deviceId && data->zoneSetUuid == zoneSetId)
|
||||
if (data->deviceId.isEqualWithNullVirtualDesktopId(deviceId) && data->zoneSetUuid == zoneSetId)
|
||||
{
|
||||
if (!IsAnotherWindowOfApplicationInstanceZoned(window, deviceId))
|
||||
{
|
||||
@@ -452,7 +496,7 @@ bool FancyZonesData::RemoveAppLastZone(HWND window, const std::wstring_view& dev
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FancyZonesData::SetAppLastZones(HWND window, const std::wstring& deviceId, const std::wstring& zoneSetId, const ZoneIndexSet& zoneIndexSet)
|
||||
bool FancyZonesData::SetAppLastZones(HWND window, const FancyZonesDataTypes::DeviceIdData& deviceId, const std::wstring& zoneSetId, const ZoneIndexSet& zoneIndexSet)
|
||||
{
|
||||
_TRACER_;
|
||||
std::scoped_lock lock{ dataLock };
|
||||
@@ -477,7 +521,7 @@ bool FancyZonesData::SetAppLastZones(HWND window, const std::wstring& deviceId,
|
||||
auto& perDesktopData = history->second;
|
||||
for (auto& data : perDesktopData)
|
||||
{
|
||||
if (data.deviceId == deviceId)
|
||||
if (data.deviceId.isEqualWithNullVirtualDesktopId(deviceId))
|
||||
{
|
||||
// application already has history on this work area, update it with new window position
|
||||
data.processIdToHandleMap[processId] = window;
|
||||
@@ -511,7 +555,7 @@ bool FancyZonesData::SetAppLastZones(HWND window, const std::wstring& deviceId,
|
||||
return true;
|
||||
}
|
||||
|
||||
void FancyZonesData::SetActiveZoneSet(const std::wstring& deviceId, const FancyZonesDataTypes::ZoneSetData& data)
|
||||
void FancyZonesData::SetActiveZoneSet(const FancyZonesDataTypes::DeviceIdData& deviceId, const FancyZonesDataTypes::ZoneSetData& data)
|
||||
{
|
||||
std::scoped_lock lock{ dataLock };
|
||||
|
||||
@@ -576,14 +620,67 @@ void FancyZonesData::SaveZoneSettings() const
|
||||
{
|
||||
_TRACER_;
|
||||
std::scoped_lock lock{ dataLock };
|
||||
JSONHelpers::SaveZoneSettings(zonesSettingsFileName, deviceInfoMap, customZoneSetsMap, quickKeysMap);
|
||||
|
||||
bool dirtyFlag = false;
|
||||
JSONHelpers::TDeviceInfoMap updatedDeviceInfoMap;
|
||||
if (m_virtualDesktopCheckCallback)
|
||||
{
|
||||
for (const auto& [id, data] : deviceInfoMap)
|
||||
{
|
||||
auto updatedId = id;
|
||||
if (!m_virtualDesktopCheckCallback(id.virtualDesktopId))
|
||||
{
|
||||
updatedId.virtualDesktopId = GUID_NULL;
|
||||
dirtyFlag = true;
|
||||
}
|
||||
|
||||
updatedDeviceInfoMap.insert({ updatedId, data });
|
||||
}
|
||||
}
|
||||
|
||||
if (dirtyFlag)
|
||||
{
|
||||
JSONHelpers::SaveZoneSettings(zonesSettingsFileName, updatedDeviceInfoMap, customZoneSetsMap, quickKeysMap);
|
||||
}
|
||||
else
|
||||
{
|
||||
JSONHelpers::SaveZoneSettings(zonesSettingsFileName, deviceInfoMap, customZoneSetsMap, quickKeysMap);
|
||||
}
|
||||
}
|
||||
|
||||
void FancyZonesData::SaveAppZoneHistory() const
|
||||
{
|
||||
_TRACER_;
|
||||
std::scoped_lock lock{ dataLock };
|
||||
JSONHelpers::SaveAppZoneHistory(appZoneHistoryFileName, appZoneHistoryMap);
|
||||
|
||||
bool dirtyFlag = false;
|
||||
std::unordered_map<std::wstring, std::vector<FancyZonesDataTypes::AppZoneHistoryData>> updatedHistory;
|
||||
if (m_virtualDesktopCheckCallback)
|
||||
{
|
||||
for (const auto& [path, dataVector] : appZoneHistoryMap)
|
||||
{
|
||||
auto updatedVector = dataVector;
|
||||
for (auto& data : updatedVector)
|
||||
{
|
||||
if (!m_virtualDesktopCheckCallback(data.deviceId.virtualDesktopId))
|
||||
{
|
||||
data.deviceId.virtualDesktopId = GUID_NULL;
|
||||
dirtyFlag = true;
|
||||
}
|
||||
}
|
||||
|
||||
updatedHistory.insert(std::make_pair(path, updatedVector));
|
||||
}
|
||||
}
|
||||
|
||||
if (dirtyFlag)
|
||||
{
|
||||
JSONHelpers::SaveAppZoneHistory(appZoneHistoryFileName, updatedHistory);
|
||||
}
|
||||
else
|
||||
{
|
||||
JSONHelpers::SaveAppZoneHistory(appZoneHistoryFileName, appZoneHistoryMap);
|
||||
}
|
||||
}
|
||||
|
||||
void FancyZonesData::SaveFancyZonesEditorParameters(bool spanZonesAcrossMonitors, const std::wstring& virtualDesktopId, const HMONITOR& targetMonitor, const std::vector<std::pair<HMONITOR, MONITORINFOEX>>& allMonitors) const
|
||||
@@ -650,14 +747,14 @@ void FancyZonesData::SaveFancyZonesEditorParameters(bool spanZonesAcrossMonitors
|
||||
json::to_file(editorParametersFileName, JSONHelpers::EditorArgs::ToJson(argsJson));
|
||||
}
|
||||
|
||||
void FancyZonesData::RemoveDesktopAppZoneHistory(const std::wstring& desktopId)
|
||||
void FancyZonesData::RemoveDesktopAppZoneHistory(GUID desktopId)
|
||||
{
|
||||
for (auto it = std::begin(appZoneHistoryMap); it != std::end(appZoneHistoryMap);)
|
||||
{
|
||||
auto& perDesktopData = it->second;
|
||||
for (auto desktopIt = std::begin(perDesktopData); desktopIt != std::end(perDesktopData);)
|
||||
{
|
||||
if (ExtractVirtualDesktopId(desktopIt->deviceId) == desktopId)
|
||||
if (desktopIt->deviceId.virtualDesktopId == desktopId)
|
||||
{
|
||||
desktopIt = perDesktopData.erase(desktopIt);
|
||||
}
|
||||
|
||||
@@ -44,14 +44,13 @@ class FancyZonesData
|
||||
public:
|
||||
FancyZonesData();
|
||||
|
||||
std::optional<FancyZonesDataTypes::DeviceInfoData> FindDeviceInfo(const std::wstring& zoneWindowId) const;
|
||||
void SetVirtualDesktopCheckCallback(std::function<bool(GUID)> callback);
|
||||
|
||||
std::optional<FancyZonesDataTypes::DeviceInfoData> FindDeviceInfo(const FancyZonesDataTypes::DeviceIdData& zoneWindowId) const;
|
||||
std::optional<FancyZonesDataTypes::CustomZoneSetData> FindCustomZoneSet(const std::wstring& guid) const;
|
||||
|
||||
const JSONHelpers::TDeviceInfoMap& GetDeviceInfoMap() const;
|
||||
|
||||
const JSONHelpers::TCustomZoneSetsMap& GetCustomZoneSetsMap() const;
|
||||
|
||||
const std::unordered_map<std::wstring, std::vector<FancyZonesDataTypes::AppZoneHistoryData>>& GetAppZoneHistoryMap() const;
|
||||
|
||||
inline const JSONHelpers::TLayoutQuickKeysMap& GetLayoutQuickKeys() const
|
||||
@@ -70,18 +69,18 @@ public:
|
||||
return settingsFileName;
|
||||
}
|
||||
|
||||
bool AddDevice(const std::wstring& deviceId);
|
||||
void CloneDeviceInfo(const std::wstring& source, const std::wstring& destination);
|
||||
void UpdatePrimaryDesktopData(const std::wstring& desktopId);
|
||||
void RemoveDeletedDesktops(const std::vector<std::wstring>& activeDesktops);
|
||||
bool AddDevice(const FancyZonesDataTypes::DeviceIdData& deviceId);
|
||||
void CloneDeviceInfo(const FancyZonesDataTypes::DeviceIdData& source, const FancyZonesDataTypes::DeviceIdData& destination);
|
||||
void SyncVirtualDesktops(GUID desktopId);
|
||||
void RemoveDeletedDesktops(const std::vector<GUID>& activeDesktops);
|
||||
|
||||
bool IsAnotherWindowOfApplicationInstanceZoned(HWND window, const std::wstring_view& deviceId) const;
|
||||
void UpdateProcessIdToHandleMap(HWND window, const std::wstring_view& deviceId);
|
||||
ZoneIndexSet GetAppLastZoneIndexSet(HWND window, const std::wstring_view& deviceId, const std::wstring_view& zoneSetId) const;
|
||||
bool RemoveAppLastZone(HWND window, const std::wstring_view& deviceId, const std::wstring_view& zoneSetId);
|
||||
bool SetAppLastZones(HWND window, const std::wstring& deviceId, const std::wstring& zoneSetId, const ZoneIndexSet& zoneIndexSet);
|
||||
bool IsAnotherWindowOfApplicationInstanceZoned(HWND window, const FancyZonesDataTypes::DeviceIdData& deviceId) const;
|
||||
void UpdateProcessIdToHandleMap(HWND window, const FancyZonesDataTypes::DeviceIdData& deviceId);
|
||||
ZoneIndexSet GetAppLastZoneIndexSet(HWND window, const FancyZonesDataTypes::DeviceIdData& deviceId, const std::wstring_view& zoneSetId) const;
|
||||
bool RemoveAppLastZone(HWND window, const FancyZonesDataTypes::DeviceIdData& deviceId, const std::wstring_view& zoneSetId);
|
||||
bool SetAppLastZones(HWND window, const FancyZonesDataTypes::DeviceIdData& deviceId, const std::wstring& zoneSetId, const ZoneIndexSet& zoneIndexSet);
|
||||
|
||||
void SetActiveZoneSet(const std::wstring& deviceId, const FancyZonesDataTypes::ZoneSetData& zoneSet);
|
||||
void SetActiveZoneSet(const FancyZonesDataTypes::DeviceIdData& deviceId, const FancyZonesDataTypes::ZoneSetData& zoneSet);
|
||||
|
||||
json::JsonObject GetPersistFancyZonesJSON();
|
||||
|
||||
@@ -100,7 +99,7 @@ private:
|
||||
friend class FancyZonesUnitTests::WorkAreaCreationUnitTests;
|
||||
friend class FancyZonesUnitTests::ZoneSetCalculateZonesUnitTests;
|
||||
|
||||
inline void SetDeviceInfo(const std::wstring& deviceId, FancyZonesDataTypes::DeviceInfoData data)
|
||||
inline void SetDeviceInfo(const FancyZonesDataTypes::DeviceIdData& deviceId, FancyZonesDataTypes::DeviceInfoData data)
|
||||
{
|
||||
deviceInfoMap[deviceId] = data;
|
||||
}
|
||||
@@ -130,7 +129,7 @@ private:
|
||||
appZoneHistoryFileName = result + L"\\" + std::wstring(L"app-zone-history.json");
|
||||
}
|
||||
#endif
|
||||
void RemoveDesktopAppZoneHistory(const std::wstring& desktopId);
|
||||
void RemoveDesktopAppZoneHistory(GUID desktopId);
|
||||
|
||||
// Maps app path to app's zone history data
|
||||
std::unordered_map<std::wstring, std::vector<FancyZonesDataTypes::AppZoneHistoryData>> appZoneHistoryMap{};
|
||||
@@ -146,6 +145,8 @@ private:
|
||||
std::wstring appZoneHistoryFileName;
|
||||
std::wstring editorParametersFileName;
|
||||
|
||||
std::function<bool(GUID)> m_virtualDesktopCheckCallback;
|
||||
|
||||
mutable std::recursive_mutex dataLock;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "pch.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "FancyZonesDataTypes.h"
|
||||
|
||||
// Non-Localizable strings
|
||||
@@ -126,4 +128,186 @@ namespace FancyZonesDataTypes
|
||||
|
||||
return high + 1;
|
||||
}
|
||||
|
||||
std::optional<DeviceIdData> DeviceIdData::ParseDeviceId(const std::wstring& str)
|
||||
{
|
||||
FancyZonesDataTypes::DeviceIdData data;
|
||||
|
||||
std::wstring temp;
|
||||
std::wstringstream wss(str);
|
||||
|
||||
/*
|
||||
Important fix for device info that contains a '_' in the name:
|
||||
1. first search for '#'
|
||||
2. Then split the remaining string by '_'
|
||||
*/
|
||||
|
||||
// Step 1: parse the name until the #, then to the '_'
|
||||
if (str.find(L'#') != std::string::npos)
|
||||
{
|
||||
std::getline(wss, temp, L'#');
|
||||
|
||||
data.deviceName = temp;
|
||||
|
||||
if (!std::getline(wss, temp, L'_'))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
data.deviceName += L"#" + temp;
|
||||
}
|
||||
else if (std::getline(wss, temp, L'_') && !temp.empty())
|
||||
{
|
||||
data.deviceName = temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Step 2: parse the rest of the id
|
||||
std::vector<std::wstring> parts;
|
||||
while (std::getline(wss, temp, L'_'))
|
||||
{
|
||||
parts.push_back(temp);
|
||||
}
|
||||
|
||||
if (parts.size() != 3 && parts.size() != 4)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/*
|
||||
Refer to ZoneWindowUtils::GenerateUniqueId parts contain:
|
||||
1. monitor id [string]
|
||||
2. width of device [int]
|
||||
3. height of device [int]
|
||||
4. virtual desktop id (GUID) [string]
|
||||
*/
|
||||
try
|
||||
{
|
||||
for (const auto& c : parts[0])
|
||||
{
|
||||
std::stoi(std::wstring(&c));
|
||||
}
|
||||
|
||||
for (const auto& c : parts[1])
|
||||
{
|
||||
std::stoi(std::wstring(&c));
|
||||
}
|
||||
|
||||
data.width = std::stoi(parts[0]);
|
||||
data.height = std::stoi(parts[1]);
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (!SUCCEEDED(CLSIDFromString(parts[2].c_str(), &data.virtualDesktopId)))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (parts.size() == 4)
|
||||
{
|
||||
data.monitorId = parts[3]; //could be empty
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
bool DeviceIdData::IsValidDeviceId(const std::wstring& str)
|
||||
{
|
||||
std::wstring monitorName;
|
||||
std::wstring temp;
|
||||
std::vector<std::wstring> parts;
|
||||
std::wstringstream wss(str);
|
||||
|
||||
/*
|
||||
Important fix for device info that contains a '_' in the name:
|
||||
1. first search for '#'
|
||||
2. Then split the remaining string by '_'
|
||||
*/
|
||||
|
||||
// Step 1: parse the name until the #, then to the '_'
|
||||
if (str.find(L'#') != std::string::npos)
|
||||
{
|
||||
std::getline(wss, temp, L'#');
|
||||
|
||||
monitorName = temp;
|
||||
|
||||
if (!std::getline(wss, temp, L'_'))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
monitorName += L"#" + temp;
|
||||
parts.push_back(monitorName);
|
||||
}
|
||||
|
||||
// Step 2: parse the rest of the id
|
||||
while (std::getline(wss, temp, L'_'))
|
||||
{
|
||||
parts.push_back(temp);
|
||||
}
|
||||
|
||||
if (parts.size() != 4)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
Refer to ZoneWindowUtils::GenerateUniqueId parts contain:
|
||||
1. monitor id [string]
|
||||
2. width of device [int]
|
||||
3. height of device [int]
|
||||
4. virtual desktop id (GUID) [string]
|
||||
*/
|
||||
try
|
||||
{
|
||||
//check if resolution contain only digits
|
||||
for (const auto& c : parts[1])
|
||||
{
|
||||
std::stoi(std::wstring(&c));
|
||||
}
|
||||
for (const auto& c : parts[2])
|
||||
{
|
||||
std::stoi(std::wstring(&c));
|
||||
}
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!FancyZonesUtils::IsValidGuid(parts[3]) || parts[0].empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::wstring DeviceIdData::toString() const
|
||||
{
|
||||
wil::unique_cotaskmem_string virtualDesktopIdStr;
|
||||
if (!SUCCEEDED(StringFromCLSID(virtualDesktopId, &virtualDesktopIdStr)))
|
||||
{
|
||||
return std::wstring();
|
||||
}
|
||||
|
||||
std::wstring result = deviceName + L"_" + std::to_wstring(width) + L"_" + std::to_wstring(height) + L"_" + virtualDesktopIdStr.get();
|
||||
if (!monitorId.empty())
|
||||
{
|
||||
result += L"_" + monitorId;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DeviceIdData::isEqualWithNullVirtualDesktopId(const DeviceIdData& other) const
|
||||
{
|
||||
return deviceName.compare(other.deviceName) == 0 && width == other.width && height == other.height && (virtualDesktopId == other.virtualDesktopId || virtualDesktopId == GUID_NULL || other.virtualDesktopId == GUID_NULL) && monitorId.compare(other.monitorId) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,24 +113,30 @@ namespace FancyZonesDataTypes
|
||||
ZoneSetLayoutType type;
|
||||
};
|
||||
|
||||
struct DeviceIdData
|
||||
{
|
||||
std::wstring deviceName = L"FallbackDevice";
|
||||
int width;
|
||||
int height;
|
||||
GUID virtualDesktopId;
|
||||
std::wstring monitorId;
|
||||
|
||||
static std::optional<DeviceIdData> ParseDeviceId(const std::wstring& str);
|
||||
static bool IsValidDeviceId(const std::wstring& str);
|
||||
|
||||
std::wstring toString() const;
|
||||
bool isEqualWithNullVirtualDesktopId(const DeviceIdData& other) const;
|
||||
};
|
||||
|
||||
struct AppZoneHistoryData
|
||||
{
|
||||
std::unordered_map<DWORD, HWND> processIdToHandleMap; // Maps process id(DWORD) of application to zoned window handle(HWND)
|
||||
|
||||
std::wstring zoneSetUuid;
|
||||
std::wstring deviceId;
|
||||
DeviceIdData deviceId;
|
||||
ZoneIndexSet zoneIndexSet;
|
||||
};
|
||||
|
||||
struct DeviceIdData
|
||||
{
|
||||
std::wstring deviceName;
|
||||
int width;
|
||||
int height;
|
||||
GUID virtualDesktopId;
|
||||
std::wstring monitorId;
|
||||
};
|
||||
|
||||
struct DeviceInfoData
|
||||
{
|
||||
ZoneSetData activeZoneSet;
|
||||
@@ -139,4 +145,31 @@ namespace FancyZonesDataTypes
|
||||
int zoneCount;
|
||||
int sensitivityRadius;
|
||||
};
|
||||
|
||||
inline bool operator==(const DeviceIdData& lhs, const DeviceIdData& rhs)
|
||||
{
|
||||
return lhs.deviceName.compare(rhs.deviceName) == 0 && lhs.width == rhs.width && lhs.height == rhs.height && lhs.virtualDesktopId == rhs.virtualDesktopId && lhs.monitorId.compare(rhs.monitorId) == 0;
|
||||
}
|
||||
|
||||
inline bool operator!=(const DeviceIdData& lhs, const DeviceIdData& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
inline bool operator<(const DeviceIdData& lhs, const DeviceIdData& rhs)
|
||||
{
|
||||
return lhs.deviceName.compare(rhs.deviceName) < 0 || lhs.width < rhs.width || lhs.height < rhs.height || lhs.monitorId.compare(rhs.monitorId) < 0;
|
||||
}
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<>
|
||||
struct hash<FancyZonesDataTypes::DeviceIdData>
|
||||
{
|
||||
size_t operator()(const FancyZonesDataTypes::DeviceIdData& Value) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
<ClInclude Include="FancyZonesWinHookEventIDs.h" />
|
||||
<ClInclude Include="GenericKeyHook.h" />
|
||||
<ClInclude Include="FancyZonesData.h" />
|
||||
<ClInclude Include="GuidUtils.h" />
|
||||
<ClInclude Include="JsonHelpers.h" />
|
||||
<ClInclude Include="KeyState.h" />
|
||||
<ClInclude Include="MonitorUtils.h" />
|
||||
@@ -105,15 +106,15 @@
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<Import Project="..\..\..\..\deps\spdlog.props" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -90,6 +90,9 @@
|
||||
<ClInclude Include="FancyZonesWindowProperties.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GuidUtils.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
|
||||
40
src/modules/fancyzones/FancyZonesLib/GuidUtils.h
Normal file
40
src/modules/fancyzones/FancyZonesLib/GuidUtils.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "gdiplus.h"
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<>
|
||||
struct hash<GUID>
|
||||
{
|
||||
size_t operator()(const GUID& Value) const
|
||||
{
|
||||
RPC_STATUS status = RPC_S_OK;
|
||||
return ::UuidHash(&const_cast<GUID&>(Value), &status);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
inline bool operator<(const GUID& guid1, const GUID& guid2)
|
||||
{
|
||||
if (guid1.Data1 != guid2.Data1)
|
||||
{
|
||||
return guid1.Data1 < guid2.Data1;
|
||||
}
|
||||
if (guid1.Data2 != guid2.Data2)
|
||||
{
|
||||
return guid1.Data2 < guid2.Data2;
|
||||
}
|
||||
if (guid1.Data3 != guid2.Data3)
|
||||
{
|
||||
return guid1.Data3 < guid2.Data3;
|
||||
}
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if (guid1.Data4[i] != guid2.Data4[i])
|
||||
{
|
||||
return guid1.Data4[i] < guid2.Data4[i];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -109,10 +109,17 @@ namespace
|
||||
data.zoneIndexSet = { static_cast<ZoneIndex>(json.GetNamedNumber(NonLocalizable::ZoneIndexStr)) };
|
||||
}
|
||||
|
||||
data.deviceId = json.GetNamedString(NonLocalizable::DeviceIdStr);
|
||||
std::wstring deviceIdStr = json.GetNamedString(NonLocalizable::DeviceIdStr).c_str();
|
||||
auto deviceId = FancyZonesDataTypes::DeviceIdData::ParseDeviceId(deviceIdStr);
|
||||
if (!deviceId.has_value())
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
data.deviceId = *deviceId;
|
||||
data.zoneSetUuid = json.GetNamedString(NonLocalizable::ZoneSetUuidStr);
|
||||
|
||||
if (!FancyZonesUtils::IsValidGuid(data.zoneSetUuid) || !FancyZonesUtils::IsValidDeviceId(data.deviceId))
|
||||
if (!FancyZonesUtils::IsValidGuid(data.zoneSetUuid))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -377,7 +384,7 @@ namespace JSONHelpers
|
||||
}
|
||||
|
||||
desktopData.SetNamedValue(NonLocalizable::ZoneIndexSetStr, jsonIndexSet);
|
||||
desktopData.SetNamedValue(NonLocalizable::DeviceIdStr, json::value(data.deviceId));
|
||||
desktopData.SetNamedValue(NonLocalizable::DeviceIdStr, json::value(data.deviceId.toString()));
|
||||
desktopData.SetNamedValue(NonLocalizable::ZoneSetUuidStr, json::value(data.zoneSetUuid));
|
||||
|
||||
appHistoryArray.Append(desktopData);
|
||||
@@ -432,7 +439,7 @@ namespace JSONHelpers
|
||||
{
|
||||
json::JsonObject result{};
|
||||
|
||||
result.SetNamedValue(NonLocalizable::DeviceIdStr, json::value(device.deviceId));
|
||||
result.SetNamedValue(NonLocalizable::DeviceIdStr, json::value(device.deviceId.toString()));
|
||||
result.SetNamedValue(NonLocalizable::ActiveZoneSetStr, JSONHelpers::ZoneSetDataJSON::ToJson(device.data.activeZoneSet));
|
||||
result.SetNamedValue(NonLocalizable::EditorShowSpacingStr, json::value(device.data.showSpacing));
|
||||
result.SetNamedValue(NonLocalizable::EditorSpacingStr, json::value(device.data.spacing));
|
||||
@@ -448,12 +455,15 @@ namespace JSONHelpers
|
||||
{
|
||||
DeviceInfoJSON result;
|
||||
|
||||
result.deviceId = device.GetNamedString(NonLocalizable::DeviceIdStr);
|
||||
if (!FancyZonesUtils::IsValidDeviceId(result.deviceId))
|
||||
std::wstring deviceIdStr = device.GetNamedString(NonLocalizable::DeviceIdStr).c_str();
|
||||
auto deviceId = FancyZonesDataTypes::DeviceIdData::ParseDeviceId(deviceIdStr);
|
||||
if (!deviceId.has_value())
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
result.deviceId = *deviceId;
|
||||
|
||||
if (auto zoneSet = JSONHelpers::ZoneSetDataJSON::FromJson(device.GetNamedObject(NonLocalizable::ActiveZoneSetStr)); zoneSet.has_value())
|
||||
{
|
||||
result.data.activeZoneSet = std::move(zoneSet.value());
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace JSONHelpers
|
||||
|
||||
struct DeviceInfoJSON
|
||||
{
|
||||
std::wstring deviceId;
|
||||
FancyZonesDataTypes::DeviceIdData deviceId;
|
||||
FancyZonesDataTypes::DeviceInfoData data;
|
||||
|
||||
static json::JsonObject ToJson(const DeviceInfoJSON& device);
|
||||
@@ -65,7 +65,7 @@ namespace JSONHelpers
|
||||
};
|
||||
|
||||
using TAppZoneHistoryMap = std::unordered_map<std::wstring, std::vector<FancyZonesDataTypes::AppZoneHistoryData>>;
|
||||
using TDeviceInfoMap = std::unordered_map<std::wstring, FancyZonesDataTypes::DeviceInfoData>;
|
||||
using TDeviceInfoMap = std::unordered_map<FancyZonesDataTypes::DeviceIdData, FancyZonesDataTypes::DeviceInfoData>;
|
||||
using TCustomZoneSetsMap = std::unordered_map<std::wstring, FancyZonesDataTypes::CustomZoneSetData>;
|
||||
using TLayoutQuickKeysMap = std::unordered_map<std::wstring, int>;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "pch.h"
|
||||
#include "MonitorWorkAreaHandler.h"
|
||||
#include "VirtualDesktop.h"
|
||||
#include "util.h"
|
||||
|
||||
winrt::com_ptr<IWorkArea> MonitorWorkAreaHandler::GetWorkArea(const GUID& desktopId, HMONITOR monitor)
|
||||
{
|
||||
|
||||
@@ -1,22 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "GuidUtils.h"
|
||||
|
||||
interface IWorkArea;
|
||||
struct ZoneColors;
|
||||
enum struct OverlappingZonesAlgorithm;
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<>
|
||||
struct hash<GUID>
|
||||
{
|
||||
size_t operator()(const GUID& Value) const
|
||||
{
|
||||
RPC_STATUS status = RPC_S_OK;
|
||||
return ::UuidHash(&const_cast<GUID&>(Value), &status);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class MonitorWorkAreaHandler
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "pch.h"
|
||||
#include "VirtualDesktop.h"
|
||||
|
||||
#include <common/logger/logger.h>
|
||||
|
||||
// Non-Localizable strings
|
||||
namespace NonLocalizable
|
||||
{
|
||||
@@ -17,6 +19,7 @@ IServiceProvider* GetServiceProvider()
|
||||
IServiceProvider* provider{ nullptr };
|
||||
if (FAILED(CoCreateInstance(CLSID_ImmersiveShell, nullptr, CLSCTX_LOCAL_SERVER, __uuidof(provider), (PVOID*)&provider)))
|
||||
{
|
||||
Logger::error("Failed to get ServiceProvider for VirtualDesktopManager");
|
||||
return nullptr;
|
||||
}
|
||||
return provider;
|
||||
@@ -28,6 +31,7 @@ IVirtualDesktopManager* GetVirtualDesktopManager()
|
||||
IServiceProvider* serviceProvider = GetServiceProvider();
|
||||
if (serviceProvider == nullptr || FAILED(serviceProvider->QueryService(__uuidof(manager), &manager)))
|
||||
{
|
||||
Logger::error("Failed to get VirtualDesktopManager");
|
||||
return nullptr;
|
||||
}
|
||||
return manager;
|
||||
@@ -77,14 +81,6 @@ std::optional<GUID> GetDesktopIdFromCurrentSession()
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool GetZoneWindowDesktopId(IWorkArea* zoneWindow, GUID* desktopId)
|
||||
{
|
||||
// Format: <device-id>_<resolution>_<virtual-desktop-id>
|
||||
std::wstring uniqueId = zoneWindow->UniqueId();
|
||||
std::wstring virtualDesktopId = uniqueId.substr(uniqueId.rfind('_') + 1);
|
||||
return SUCCEEDED(CLSIDFromString(virtualDesktopId.c_str(), desktopId));
|
||||
}
|
||||
|
||||
HKEY OpenVirtualDesktopsRegKey()
|
||||
{
|
||||
HKEY hKey{ nullptr };
|
||||
@@ -124,18 +120,7 @@ void VirtualDesktop::UnInit()
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<GUID> VirtualDesktop::GetWindowDesktopId(HWND topLevelWindow) const
|
||||
{
|
||||
GUID desktopId{};
|
||||
if (m_vdManager && SUCCEEDED(m_vdManager->GetWindowDesktopId(topLevelWindow, &desktopId)))
|
||||
{
|
||||
return desktopId;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<GUID> VirtualDesktop::GetCurrentVirtualDesktopId() const
|
||||
std::optional<GUID> VirtualDesktop::GetCurrentVirtualDesktopIdFromRegistry() const
|
||||
{
|
||||
// On newer Windows builds, the current virtual desktop is persisted to
|
||||
// a totally different reg key. Look there first.
|
||||
@@ -160,19 +145,17 @@ std::optional<GUID> VirtualDesktop::GetCurrentVirtualDesktopId() const
|
||||
// switch occurred in current session.
|
||||
else
|
||||
{
|
||||
auto ids = GetVirtualDesktopIds();
|
||||
auto ids = GetVirtualDesktopIdsFromRegistry();
|
||||
if (ids.has_value() && ids->size() > 0)
|
||||
{
|
||||
return ids->at(0);
|
||||
}
|
||||
}
|
||||
|
||||
desktopId = GetDesktopIdByTopLevelWindows();
|
||||
|
||||
return desktopId;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::vector<GUID>> VirtualDesktop::GetVirtualDesktopIds(HKEY hKey) const
|
||||
std::optional<std::vector<GUID>> VirtualDesktop::GetVirtualDesktopIdsFromRegistry(HKEY hKey) const
|
||||
{
|
||||
if (!hKey)
|
||||
{
|
||||
@@ -205,9 +188,9 @@ std::optional<std::vector<GUID>> VirtualDesktop::GetVirtualDesktopIds(HKEY hKey)
|
||||
return temp;
|
||||
}
|
||||
|
||||
std::optional<std::vector<GUID>> VirtualDesktop::GetVirtualDesktopIds() const
|
||||
std::optional<std::vector<GUID>> VirtualDesktop::GetVirtualDesktopIdsFromRegistry() const
|
||||
{
|
||||
return GetVirtualDesktopIds(GetVirtualDesktopsRegKey());
|
||||
return GetVirtualDesktopIdsFromRegistry(GetVirtualDesktopsRegKey());
|
||||
}
|
||||
|
||||
bool VirtualDesktop::IsWindowOnCurrentDesktop(HWND window) const
|
||||
@@ -232,25 +215,50 @@ std::optional<GUID> VirtualDesktop::GetDesktopId(HWND window) const
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<GUID> VirtualDesktop::GetDesktopIdByTopLevelWindows() const
|
||||
std::vector<std::pair<HWND, GUID>> VirtualDesktop::GetWindowsRelatedToDesktops() const
|
||||
{
|
||||
using result_t = std::vector<HWND>;
|
||||
result_t result;
|
||||
result_t windows;
|
||||
|
||||
auto callback = [](HWND window, LPARAM data) -> BOOL {
|
||||
result_t& result = *reinterpret_cast<result_t*>(data);
|
||||
result.push_back(window);
|
||||
return TRUE;
|
||||
};
|
||||
EnumWindows(callback, reinterpret_cast<LPARAM>(&result));
|
||||
EnumWindows(callback, reinterpret_cast<LPARAM>(&windows));
|
||||
|
||||
for (const auto window : result)
|
||||
std::vector<std::pair<HWND, GUID>> result;
|
||||
for (auto window : windows)
|
||||
{
|
||||
auto desktop = GetDesktopId(window);
|
||||
if (desktop.has_value())
|
||||
{
|
||||
result.push_back({ window, *desktop });
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<GUID> VirtualDesktop::GetDesktopIdByTopLevelWindows() const
|
||||
{
|
||||
using result_t = std::vector<HWND>;
|
||||
result_t windows;
|
||||
|
||||
auto callback = [](HWND window, LPARAM data) -> BOOL {
|
||||
result_t& result = *reinterpret_cast<result_t*>(data);
|
||||
result.push_back(window);
|
||||
return TRUE;
|
||||
};
|
||||
EnumWindows(callback, reinterpret_cast<LPARAM>(&windows));
|
||||
|
||||
for (const auto window : windows)
|
||||
{
|
||||
std::optional<GUID> id = GetDesktopId(window);
|
||||
if (id.has_value())
|
||||
{
|
||||
// Otherwise keep checking other windows
|
||||
return id;
|
||||
return *id;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,15 +9,36 @@ public:
|
||||
VirtualDesktop(const std::function<void()>& vdInitCallback, const std::function<void()>& vdUpdatedCallback);
|
||||
~VirtualDesktop() = default;
|
||||
|
||||
inline bool IsVirtualDesktopIdSavedInRegistry(GUID id) const
|
||||
{
|
||||
auto ids = GetVirtualDesktopIdsFromRegistry();
|
||||
if (!ids.has_value())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& regId : *ids)
|
||||
{
|
||||
if (regId == id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Init();
|
||||
void UnInit();
|
||||
|
||||
std::optional<GUID> GetWindowDesktopId(HWND topLevelWindow) const;
|
||||
std::optional<GUID> GetCurrentVirtualDesktopId() const;
|
||||
std::optional<std::vector<GUID>> GetVirtualDesktopIds() const;
|
||||
std::optional<GUID> GetCurrentVirtualDesktopIdFromRegistry() const;
|
||||
std::optional<std::vector<GUID>> GetVirtualDesktopIdsFromRegistry() const;
|
||||
|
||||
bool IsWindowOnCurrentDesktop(HWND window) const;
|
||||
std::optional<GUID> GetDesktopId(HWND window) const;
|
||||
std::optional<GUID> GetDesktopIdByTopLevelWindows() const;
|
||||
|
||||
std::vector<std::pair<HWND, GUID>> GetWindowsRelatedToDesktops() const;
|
||||
|
||||
private:
|
||||
std::function<void()> m_vdInitCallback;
|
||||
@@ -28,7 +49,6 @@ private:
|
||||
OnThreadExecutor m_virtualDesktopTrackerThread;
|
||||
wil::unique_handle m_terminateVirtualDesktopTrackerEvent;
|
||||
|
||||
std::optional<std::vector<GUID>> GetVirtualDesktopIds(HKEY hKey) const;
|
||||
std::optional<GUID> GetDesktopIdByTopLevelWindows() const;
|
||||
std::optional<std::vector<GUID>> GetVirtualDesktopIdsFromRegistry(HKEY hKey) const;
|
||||
void HandleVirtualDesktopUpdates();
|
||||
};
|
||||
|
||||
@@ -109,7 +109,7 @@ public:
|
||||
WorkArea(HINSTANCE hinstance);
|
||||
~WorkArea();
|
||||
|
||||
bool Init(HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, const std::wstring& parentUniqueId, const ZoneColors& zoneColors, OverlappingZonesAlgorithm overlappingAlgorithm);
|
||||
bool Init(HINSTANCE hinstance, HMONITOR monitor, const FancyZonesDataTypes::DeviceIdData& uniqueId, const FancyZonesDataTypes::DeviceIdData& parentUniqueId, const ZoneColors& zoneColors, OverlappingZonesAlgorithm overlappingAlgorithm);
|
||||
|
||||
IFACEMETHODIMP MoveSizeEnter(HWND window) noexcept;
|
||||
IFACEMETHODIMP MoveSizeUpdate(POINT const& ptScreen, bool dragEnabled, bool selectManyZones) noexcept;
|
||||
@@ -124,7 +124,7 @@ public:
|
||||
MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCode, bool cycle) noexcept;
|
||||
IFACEMETHODIMP_(bool)
|
||||
ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode) noexcept;
|
||||
IFACEMETHODIMP_(std::wstring)
|
||||
IFACEMETHODIMP_(FancyZonesDataTypes::DeviceIdData)
|
||||
UniqueId() const noexcept { return { m_uniqueId }; }
|
||||
IFACEMETHODIMP_(void)
|
||||
SaveWindowProcessToZoneIndex(HWND window) noexcept;
|
||||
@@ -151,7 +151,7 @@ protected:
|
||||
static LRESULT CALLBACK s_WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) noexcept;
|
||||
|
||||
private:
|
||||
void InitializeZoneSets(const std::wstring& parentUniqueId) noexcept;
|
||||
void InitializeZoneSets(const FancyZonesDataTypes::DeviceIdData& parentUniqueId) noexcept;
|
||||
void CalculateZoneSet(OverlappingZonesAlgorithm overlappingAlgorithm) noexcept;
|
||||
void UpdateActiveZoneSet(_In_opt_ IZoneSet* zoneSet) noexcept;
|
||||
LRESULT WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept;
|
||||
@@ -159,7 +159,7 @@ private:
|
||||
void SetAsTopmostWindow() noexcept;
|
||||
|
||||
HMONITOR m_monitor{};
|
||||
std::wstring m_uniqueId; // Parsed deviceId + resolution + virtualDesktopId
|
||||
FancyZonesDataTypes::DeviceIdData m_uniqueId;
|
||||
HWND m_window{}; // Hidden tool window used to represent current monitor desktop work area.
|
||||
HWND m_windowMoveSize{};
|
||||
winrt::com_ptr<IZoneSet> m_activeZoneSet;
|
||||
@@ -189,7 +189,7 @@ WorkArea::~WorkArea()
|
||||
windowPool.FreeZoneWindow(m_window);
|
||||
}
|
||||
|
||||
bool WorkArea::Init(HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, const std::wstring& parentUniqueId, const ZoneColors& zoneColors, OverlappingZonesAlgorithm overlappingAlgorithm)
|
||||
bool WorkArea::Init(HINSTANCE hinstance, HMONITOR monitor, const FancyZonesDataTypes::DeviceIdData& uniqueId, const FancyZonesDataTypes::DeviceIdData& parentUniqueId, const ZoneColors& zoneColors, OverlappingZonesAlgorithm overlappingAlgorithm)
|
||||
{
|
||||
m_zoneColors = zoneColors;
|
||||
m_overlappingAlgorithm = overlappingAlgorithm;
|
||||
@@ -467,11 +467,17 @@ WorkArea::SetOverlappingZonesAlgorithm(OverlappingZonesAlgorithm overlappingAlgo
|
||||
|
||||
#pragma region private
|
||||
|
||||
void WorkArea::InitializeZoneSets(const std::wstring& parentUniqueId) noexcept
|
||||
void WorkArea::InitializeZoneSets(const FancyZonesDataTypes::DeviceIdData& parentUniqueId) noexcept
|
||||
{
|
||||
wil::unique_cotaskmem_string virtualDesktopId;
|
||||
if (SUCCEEDED(StringFromCLSID(m_uniqueId.virtualDesktopId, &virtualDesktopId)))
|
||||
{
|
||||
Logger::debug(L"Initialize zone sets on the virtual desktop {}", virtualDesktopId.get());
|
||||
}
|
||||
|
||||
bool deviceAdded = FancyZonesDataInstance().AddDevice(m_uniqueId);
|
||||
// If the device has been added, check if it should inherit the parent's layout
|
||||
if (deviceAdded && !parentUniqueId.empty())
|
||||
if (deviceAdded && parentUniqueId.virtualDesktopId != GUID_NULL)
|
||||
{
|
||||
FancyZonesDataInstance().CloneDeviceInfo(parentUniqueId, m_uniqueId);
|
||||
}
|
||||
@@ -618,7 +624,7 @@ LRESULT CALLBACK WorkArea::s_WndProc(HWND window, UINT message, WPARAM wparam, L
|
||||
DefWindowProc(window, message, wparam, lparam);
|
||||
}
|
||||
|
||||
winrt::com_ptr<IWorkArea> MakeWorkArea(HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, const std::wstring& parentUniqueId, const ZoneColors& zoneColors, OverlappingZonesAlgorithm overlappingAlgorithm) noexcept
|
||||
winrt::com_ptr<IWorkArea> MakeWorkArea(HINSTANCE hinstance, HMONITOR monitor, const FancyZonesDataTypes::DeviceIdData& uniqueId, const FancyZonesDataTypes::DeviceIdData& parentUniqueId, const ZoneColors& zoneColors, OverlappingZonesAlgorithm overlappingAlgorithm) noexcept
|
||||
{
|
||||
auto self = winrt::make_self<WorkArea>(hinstance);
|
||||
if (self->Init(hinstance, monitor, uniqueId, parentUniqueId, zoneColors, overlappingAlgorithm))
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "FancyZones.h"
|
||||
#include "FancyZonesLib/ZoneSet.h"
|
||||
#include "FancyZonesLib/ZoneColors.h"
|
||||
#include "FancyZonesLib/FancyZonesDataTypes.h"
|
||||
|
||||
/**
|
||||
* Class representing single work area, which is defined by monitor and virtual desktop.
|
||||
@@ -97,7 +98,7 @@ interface __declspec(uuid("{7F017528-8110-4FB3-BE41-F472969C2560}")) IWorkArea :
|
||||
/**
|
||||
* @returns Unique work area identifier. Format: <device-id>_<resolution>_<virtual-desktop-id>
|
||||
*/
|
||||
IFACEMETHOD_(std::wstring, UniqueId)() const = 0;
|
||||
IFACEMETHOD_(FancyZonesDataTypes::DeviceIdData, UniqueId)() const = 0;
|
||||
/**
|
||||
* @returns Active zone layout for this work area.
|
||||
*/
|
||||
@@ -130,4 +131,4 @@ interface __declspec(uuid("{7F017528-8110-4FB3-BE41-F472969C2560}")) IWorkArea :
|
||||
IFACEMETHOD_(void, SetOverlappingZonesAlgorithm)(OverlappingZonesAlgorithm overlappingAlgorithm) = 0;
|
||||
};
|
||||
|
||||
winrt::com_ptr<IWorkArea> MakeWorkArea(HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, const std::wstring& parentUniqueId, const ZoneColors& zoneColors, OverlappingZonesAlgorithm overlappingAlgorithm) noexcept;
|
||||
winrt::com_ptr<IWorkArea> MakeWorkArea(HINSTANCE hinstance, HMONITOR monitor, const FancyZonesDataTypes::DeviceIdData& uniqueId, const FancyZonesDataTypes::DeviceIdData& parentUniqueId, const ZoneColors& zoneColors, OverlappingZonesAlgorithm overlappingAlgorithm) noexcept;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.200729.8" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.200902.2" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.210204.1" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <common/utils/window.h>
|
||||
|
||||
#include <array>
|
||||
#include <sstream>
|
||||
#include <complex>
|
||||
#include <wil/Resource.h>
|
||||
|
||||
@@ -84,94 +83,6 @@ namespace FancyZonesUtils
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<FancyZonesDataTypes::DeviceIdData> ParseDeviceId(const std::wstring& str)
|
||||
{
|
||||
FancyZonesDataTypes::DeviceIdData data;
|
||||
|
||||
std::wstring temp;
|
||||
std::wstringstream wss(str);
|
||||
|
||||
/*
|
||||
Important fix for device info that contains a '_' in the name:
|
||||
1. first search for '#'
|
||||
2. Then split the remaining string by '_'
|
||||
*/
|
||||
|
||||
// Step 1: parse the name until the #, then to the '_'
|
||||
if (str.find(L'#') != std::string::npos)
|
||||
{
|
||||
std::getline(wss, temp, L'#');
|
||||
|
||||
data.deviceName = temp;
|
||||
|
||||
if (!std::getline(wss, temp, L'_'))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
data.deviceName += L"#" + temp;
|
||||
}
|
||||
else if (std::getline(wss, temp, L'_') && !temp.empty())
|
||||
{
|
||||
data.deviceName = temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Step 2: parse the rest of the id
|
||||
std::vector<std::wstring> parts;
|
||||
while (std::getline(wss, temp, L'_'))
|
||||
{
|
||||
parts.push_back(temp);
|
||||
}
|
||||
|
||||
if (parts.size() != 3 && parts.size() != 4)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/*
|
||||
Refer to ZoneWindowUtils::GenerateUniqueId parts contain:
|
||||
1. monitor id [string]
|
||||
2. width of device [int]
|
||||
3. height of device [int]
|
||||
4. virtual desktop id (GUID) [string]
|
||||
*/
|
||||
try
|
||||
{
|
||||
for (const auto& c : parts[0])
|
||||
{
|
||||
std::stoi(std::wstring(&c));
|
||||
}
|
||||
|
||||
for (const auto& c : parts[1])
|
||||
{
|
||||
std::stoi(std::wstring(&c));
|
||||
}
|
||||
|
||||
data.width = std::stoi(parts[0]);
|
||||
data.height = std::stoi(parts[1]);
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (!SUCCEEDED(CLSIDFromString(parts[2].c_str(), &data.virtualDesktopId)))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (parts.size() == 4)
|
||||
{
|
||||
data.monitorId = parts[3]; //could be empty
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
typedef BOOL(WINAPI* GetDpiForMonitorInternalFunc)(HMONITOR, UINT, UINT*, UINT*);
|
||||
|
||||
std::wstring GetDisplayDeviceId(const std::wstring& device, std::unordered_map<std::wstring, DWORD>& displayDeviceIdxMap)
|
||||
@@ -617,78 +528,6 @@ namespace FancyZonesUtils
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool IsValidDeviceId(const std::wstring& str)
|
||||
{
|
||||
std::wstring monitorName;
|
||||
std::wstring temp;
|
||||
std::vector<std::wstring> parts;
|
||||
std::wstringstream wss(str);
|
||||
|
||||
/*
|
||||
Important fix for device info that contains a '_' in the name:
|
||||
1. first search for '#'
|
||||
2. Then split the remaining string by '_'
|
||||
*/
|
||||
|
||||
// Step 1: parse the name until the #, then to the '_'
|
||||
if (str.find(L'#') != std::string::npos)
|
||||
{
|
||||
std::getline(wss, temp, L'#');
|
||||
|
||||
monitorName = temp;
|
||||
|
||||
if (!std::getline(wss, temp, L'_'))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
monitorName += L"#" + temp;
|
||||
parts.push_back(monitorName);
|
||||
}
|
||||
|
||||
// Step 2: parse the rest of the id
|
||||
while (std::getline(wss, temp, L'_'))
|
||||
{
|
||||
parts.push_back(temp);
|
||||
}
|
||||
|
||||
if (parts.size() != 4)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
Refer to ZoneWindowUtils::GenerateUniqueId parts contain:
|
||||
1. monitor id [string]
|
||||
2. width of device [int]
|
||||
3. height of device [int]
|
||||
4. virtual desktop id (GUID) [string]
|
||||
*/
|
||||
try
|
||||
{
|
||||
//check if resolution contain only digits
|
||||
for (const auto& c : parts[1])
|
||||
{
|
||||
std::stoi(std::wstring(&c));
|
||||
}
|
||||
for (const auto& c : parts[2])
|
||||
{
|
||||
std::stoi(std::wstring(&c));
|
||||
}
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsValidGuid(parts[3]) || parts[0].empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::wstring GenerateUniqueId(HMONITOR monitor, const std::wstring& deviceId, const std::wstring& virtualDesktopId)
|
||||
{
|
||||
MONITORINFOEXW mi;
|
||||
|
||||
@@ -207,10 +207,7 @@ namespace FancyZonesUtils
|
||||
|
||||
std::wstring GenerateUniqueId(HMONITOR monitor, const std::wstring& devideId, const std::wstring& virtualDesktopId);
|
||||
std::wstring GenerateUniqueIdAllMonitorsArea(const std::wstring& virtualDesktopId);
|
||||
|
||||
std::wstring TrimDeviceId(const std::wstring& deviceId);
|
||||
std::optional<FancyZonesDataTypes::DeviceIdData> ParseDeviceId(const std::wstring& deviceId);
|
||||
bool IsValidDeviceId(const std::wstring& str);
|
||||
|
||||
RECT PrepareRectForCycling(RECT windowRect, RECT zoneWindowRect, DWORD vkCode) noexcept;
|
||||
size_t ChooseNextZoneByPosition(DWORD vkCode, RECT windowRect, const std::vector<RECT>& zoneRects) noexcept;
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
@@ -69,8 +69,8 @@
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.200729.8" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.200902.2" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.210204.1" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -88,61 +88,61 @@ namespace FancyZonesUnitTests
|
||||
TEST_METHOD (DeviceId)
|
||||
{
|
||||
const auto deviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
|
||||
Assert::IsTrue(IsValidDeviceId(deviceId));
|
||||
Assert::IsTrue(FancyZonesDataTypes::DeviceIdData::IsValidDeviceId(deviceId));
|
||||
}
|
||||
|
||||
TEST_METHOD (DeviceIdWithoutHashInName)
|
||||
{
|
||||
const auto deviceId = L"LOCALDISPLAY_5120_1440_{00000000-0000-0000-0000-000000000000}";
|
||||
Assert::IsTrue(IsValidDeviceId(deviceId));
|
||||
Assert::IsTrue(FancyZonesDataTypes::DeviceIdData::IsValidDeviceId(deviceId));
|
||||
}
|
||||
|
||||
TEST_METHOD (DeviceIdWithoutHashInNameButWithUnderscores)
|
||||
{
|
||||
const auto deviceId = L"LOCAL_DISPLAY_5120_1440_{00000000-0000-0000-0000-000000000000}";
|
||||
Assert::IsFalse(IsValidDeviceId(deviceId));
|
||||
Assert::IsFalse(FancyZonesDataTypes::DeviceIdData::IsValidDeviceId(deviceId));
|
||||
}
|
||||
|
||||
TEST_METHOD (DeviceIdWithUnderscoresInName)
|
||||
{
|
||||
const auto deviceId = L"Default_Monitor#1&1f0c3c2f&0&UID256_5120_1440_{00000000-0000-0000-0000-000000000000}";
|
||||
Assert::IsTrue(IsValidDeviceId(deviceId));
|
||||
Assert::IsTrue(FancyZonesDataTypes::DeviceIdData::IsValidDeviceId(deviceId));
|
||||
}
|
||||
|
||||
TEST_METHOD (DeviceIdInvalidFormat)
|
||||
{
|
||||
const auto deviceId = L"_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
|
||||
Assert::IsFalse(IsValidDeviceId(deviceId));
|
||||
Assert::IsFalse(FancyZonesDataTypes::DeviceIdData::IsValidDeviceId(deviceId));
|
||||
}
|
||||
|
||||
TEST_METHOD (DeviceIdInvalidFormat2)
|
||||
{
|
||||
const auto deviceId = L"AOC2460#4&fe3a015&0&UID65793_19201200_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
|
||||
Assert::IsFalse(IsValidDeviceId(deviceId));
|
||||
Assert::IsFalse(FancyZonesDataTypes::DeviceIdData::IsValidDeviceId(deviceId));
|
||||
}
|
||||
|
||||
TEST_METHOD (DeviceIdInvalidDecimals)
|
||||
{
|
||||
const auto deviceId = L"AOC2460#4&fe3a015&0&UID65793_aaaa_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
|
||||
Assert::IsFalse(IsValidDeviceId(deviceId));
|
||||
Assert::IsFalse(FancyZonesDataTypes::DeviceIdData::IsValidDeviceId(deviceId));
|
||||
}
|
||||
|
||||
TEST_METHOD (DeviceIdInvalidDecimals2)
|
||||
{
|
||||
const auto deviceId = L"AOC2460#4&fe3a015&0&UID65793_19a0_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
|
||||
Assert::IsFalse(IsValidDeviceId(deviceId));
|
||||
Assert::IsFalse(FancyZonesDataTypes::DeviceIdData::IsValidDeviceId(deviceId));
|
||||
}
|
||||
|
||||
TEST_METHOD (DeviceIdInvalidDecimals3)
|
||||
{
|
||||
const auto deviceId = L"AOC2460#4&fe3a015&0&UID65793_1900_120000000000000_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
|
||||
Assert::IsFalse(IsValidDeviceId(deviceId));
|
||||
Assert::IsFalse(FancyZonesDataTypes::DeviceIdData::IsValidDeviceId(deviceId));
|
||||
}
|
||||
|
||||
TEST_METHOD (DeviceIdInvalidGuid)
|
||||
{
|
||||
const auto deviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-4B5D-8851-4791D66B1539}";
|
||||
Assert::IsFalse(IsValidDeviceId(deviceId));
|
||||
Assert::IsFalse(FancyZonesDataTypes::DeviceIdData::IsValidDeviceId(deviceId));
|
||||
}
|
||||
};
|
||||
TEST_CLASS (ZoneSetLayoutTypeUnitTest)
|
||||
@@ -779,7 +779,7 @@ namespace FancyZonesUnitTests
|
||||
.zoneSetUuid = L"zoneset-uuid", .deviceId = L"device-id", .zoneIndexSet = { 54321 }
|
||||
};
|
||||
AppZoneHistoryJSON appZoneHistory{ L"appPath", std::vector<AppZoneHistoryData>{ data } };
|
||||
json::JsonObject expected = json::JsonObject::Parse(L"{\"app-path\": \"appPath\", \"history\":[{\"zone-index-set\": [54321], \"device-id\": \"device-id\", \"zoneset-uuid\": \"zoneset-uuid\"}]}");
|
||||
json::JsonObject expected = json::JsonObject::Parse(L"{\"app-path\": \"appPath\", \"history\":[{\"zone-index-set\": [54321], \"device-id\": \"device-id_0_0_{00000000-0000-0000-0000-000000000000}\", \"zoneset-uuid\": \"zoneset-uuid\"}]}");
|
||||
|
||||
auto actual = AppZoneHistoryJSON::ToJson(appZoneHistory);
|
||||
compareJsonObjects(expected, actual);
|
||||
@@ -788,7 +788,8 @@ namespace FancyZonesUnitTests
|
||||
TEST_METHOD (FromJson)
|
||||
{
|
||||
AppZoneHistoryData data{
|
||||
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502906}", .deviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}", .zoneIndexSet = { 54321 }
|
||||
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502906}", .deviceId = DeviceIdData::ParseDeviceId(L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}").value(), .zoneIndexSet = {
|
||||
54321 }
|
||||
};
|
||||
AppZoneHistoryJSON expected{ L"appPath", std::vector<AppZoneHistoryData>{ data } };
|
||||
json::JsonObject json = json::JsonObject::Parse(L"{\"app-path\": \"appPath\", \"history\": [{\"device-id\": \"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}\", \"zoneset-uuid\": \"{33A2B101-06E0-437B-A61E-CDBECF502906}\", \"zone-index\": 54321}]}");
|
||||
@@ -799,7 +800,7 @@ namespace FancyZonesUnitTests
|
||||
Assert::AreEqual(expected.appPath.c_str(), actual->appPath.c_str());
|
||||
Assert::AreEqual(expected.data.size(), actual->data.size());
|
||||
Assert::IsTrue(expected.data[0].zoneIndexSet == actual->data[0].zoneIndexSet);
|
||||
Assert::AreEqual(expected.data[0].deviceId.c_str(), actual->data[0].deviceId.c_str());
|
||||
Assert::IsTrue(expected.data[0].deviceId == actual->data[0].deviceId);
|
||||
Assert::AreEqual(expected.data[0].zoneSetUuid.c_str(), actual->data[0].zoneSetUuid.c_str());
|
||||
}
|
||||
|
||||
@@ -855,7 +856,7 @@ namespace FancyZonesUnitTests
|
||||
AppZoneHistoryJSON appZoneHistory{
|
||||
L"appPath", std::vector<AppZoneHistoryData>{ data1, data2 }
|
||||
};
|
||||
json::JsonObject expected = json::JsonObject::Parse(L"{\"app-path\": \"appPath\", \"history\": [{\"zone-index-set\": [54321], \"device-id\": \"device-id1\", \"zoneset-uuid\": \"zoneset-uuid1\"}, {\"zone-index-set\": [12345], \"device-id\": \"device-id2\", \"zoneset-uuid\": \"zoneset-uuid2\"}]}");
|
||||
json::JsonObject expected = json::JsonObject::Parse(L"{\"app-path\": \"appPath\", \"history\": [{\"zone-index-set\": [54321], \"device-id\": \"device-id1_0_0_{00000000-0000-0000-0000-000000000000}\", \"zoneset-uuid\": \"zoneset-uuid1\"}, {\"zone-index-set\": [12345], \"device-id\": \"device-id2_0_0_{00000000-0000-0000-0000-000000000000}\", \"zoneset-uuid\": \"zoneset-uuid2\"}]}");
|
||||
|
||||
auto actual = AppZoneHistoryJSON::ToJson(appZoneHistory);
|
||||
std::wstring s = actual.Stringify().c_str();
|
||||
@@ -865,10 +866,11 @@ namespace FancyZonesUnitTests
|
||||
TEST_METHOD (FromJsonMultipleDesktopAppHistory)
|
||||
{
|
||||
AppZoneHistoryData data1{
|
||||
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502906}", .deviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}", .zoneIndexSet = { 54321 }
|
||||
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502906}", .deviceId = DeviceIdData::ParseDeviceId(L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}").value(), .zoneIndexSet = { 54321 }
|
||||
};
|
||||
AppZoneHistoryData data2{
|
||||
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502906}", .deviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{8a0b9205-6128-45a2-934a-b97f5b271235}", .zoneIndexSet = { 12345 }
|
||||
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502906}", .deviceId = DeviceIdData::ParseDeviceId(L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{8a0b9205-6128-45a2-934a-b97f5b271235}").value(), .zoneIndexSet = {
|
||||
12345 }
|
||||
};
|
||||
AppZoneHistoryJSON expected{
|
||||
L"appPath", std::vector<AppZoneHistoryData>{ data1, data2 }
|
||||
@@ -884,7 +886,7 @@ namespace FancyZonesUnitTests
|
||||
for (size_t i = 0; i < expected.data.size(); ++i)
|
||||
{
|
||||
Assert::IsTrue(expected.data[i].zoneIndexSet == actual->data[i].zoneIndexSet);
|
||||
Assert::AreEqual(expected.data[i].deviceId.c_str(), actual->data[i].deviceId.c_str());
|
||||
Assert::IsTrue(expected.data[i].deviceId == actual->data[i].deviceId);
|
||||
Assert::AreEqual(expected.data[i].zoneSetUuid.c_str(), actual->data[i].zoneSetUuid.c_str());
|
||||
}
|
||||
}
|
||||
@@ -893,10 +895,16 @@ namespace FancyZonesUnitTests
|
||||
TEST_CLASS (DeviceInfoUnitTests)
|
||||
{
|
||||
private:
|
||||
const std::wstring m_defaultDeviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
|
||||
FancyZonesDataTypes::DeviceIdData m_defaultDeviceId{ L"AOC2460#4&fe3a015&0&UID65793", 1920, 1200, };
|
||||
DeviceInfoJSON m_defaultDeviceInfo = DeviceInfoJSON{ m_defaultDeviceId, DeviceInfoData{ ZoneSetData{ L"{33A2B101-06E0-437B-A61E-CDBECF502906}", ZoneSetLayoutType::Custom }, true, 16, 3 } };
|
||||
json::JsonObject m_defaultJson = json::JsonObject::Parse(L"{\"device-id\": \"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}\", \"active-zoneset\": {\"type\": \"custom\", \"uuid\": \"{33A2B101-06E0-437B-A61E-CDBECF502906}\"}, \"editor-show-spacing\": true, \"editor-spacing\": 16, \"editor-zone-count\": 3}");
|
||||
|
||||
TEST_METHOD_INITIALIZE(Init)
|
||||
{
|
||||
CLSIDFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}", &m_defaultDeviceId.virtualDesktopId);
|
||||
m_defaultDeviceInfo.deviceId = m_defaultDeviceId;
|
||||
}
|
||||
|
||||
public:
|
||||
TEST_METHOD (ToJson)
|
||||
{
|
||||
@@ -916,7 +924,7 @@ namespace FancyZonesUnitTests
|
||||
auto actual = DeviceInfoJSON::FromJson(json);
|
||||
Assert::IsTrue(actual.has_value());
|
||||
|
||||
Assert::AreEqual(expected.deviceId.c_str(), actual->deviceId.c_str(), L"device id");
|
||||
Assert::IsTrue(expected.deviceId == actual->deviceId);
|
||||
Assert::AreEqual(expected.data.zoneCount, actual->data.zoneCount, L"zone count");
|
||||
Assert::AreEqual((int)expected.data.activeZoneSet.type, (int)actual->data.activeZoneSet.type, L"zone set type");
|
||||
Assert::AreEqual(expected.data.activeZoneSet.uuid.c_str(), actual->data.activeZoneSet.uuid.c_str(), L"zone set uuid");
|
||||
@@ -1016,11 +1024,13 @@ namespace FancyZonesUnitTests
|
||||
{
|
||||
private:
|
||||
const std::wstring_view m_moduleName = L"FancyZonesUnitTests";
|
||||
const std::wstring m_defaultDeviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
|
||||
const std::wstring m_defaultDeviceIdStr = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
|
||||
const std::wstring m_defaultCustomDeviceStr = L"{\"device-id\": \"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}\", \"active-zoneset\": {\"type\": \"custom\", \"uuid\": \"{33A2B101-06E0-437B-A61E-CDBECF502906}\"}, \"editor-show-spacing\": true, \"editor-spacing\": 16, \"editor-zone-count\": 3}";
|
||||
const json::JsonValue m_defaultCustomDeviceValue = json::JsonValue::Parse(m_defaultCustomDeviceStr);
|
||||
const json::JsonObject m_defaultCustomDeviceObj = json::JsonObject::Parse(m_defaultCustomDeviceStr);
|
||||
|
||||
const FancyZonesDataTypes::DeviceIdData m_defaultDeviceId = FancyZonesDataTypes::DeviceIdData::ParseDeviceId(m_defaultDeviceIdStr).value();
|
||||
|
||||
GUID m_defaultVDId;
|
||||
|
||||
HINSTANCE m_hInst{};
|
||||
@@ -1177,7 +1187,7 @@ namespace FancyZonesUnitTests
|
||||
TEST_METHOD (AppZoneHistoryParseSingle)
|
||||
{
|
||||
const std::wstring expectedAppPath = L"appPath";
|
||||
const std::wstring expectedDeviceId = m_defaultDeviceId;
|
||||
const auto expectedDeviceId = m_defaultDeviceId;
|
||||
const std::wstring expectedZoneSetId = L"{33A2B101-06E0-437B-A61E-CDBECF502906}";
|
||||
const size_t expectedIndex = 54321;
|
||||
|
||||
@@ -1200,7 +1210,7 @@ namespace FancyZonesUnitTests
|
||||
const auto entryData = entry->second;
|
||||
Assert::AreEqual(expected.data.size(), entryData.size());
|
||||
Assert::AreEqual(expectedZoneSetId.c_str(), entryData[0].zoneSetUuid.c_str());
|
||||
Assert::AreEqual(expectedDeviceId.c_str(), entryData[0].deviceId.c_str());
|
||||
Assert::IsTrue(expectedDeviceId == entryData[0].deviceId);
|
||||
Assert::IsTrue(std::vector<ZoneIndex>{ expectedIndex } == entryData[0].zoneIndexSet);
|
||||
}
|
||||
|
||||
@@ -1209,16 +1219,20 @@ namespace FancyZonesUnitTests
|
||||
json::JsonObject json;
|
||||
json::JsonArray zoneHistoryArray;
|
||||
AppZoneHistoryData data1{
|
||||
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502900}", .deviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1530}", .zoneIndexSet = { 1 }
|
||||
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502900}", .deviceId = DeviceIdData::ParseDeviceId(L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1530}").value(), .zoneIndexSet = {
|
||||
1 }
|
||||
};
|
||||
AppZoneHistoryData data2{
|
||||
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502901}", .deviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1531}", .zoneIndexSet = { 2 }
|
||||
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502901}", .deviceId = DeviceIdData::ParseDeviceId(L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1531}").value(), .zoneIndexSet = {
|
||||
2 }
|
||||
};
|
||||
AppZoneHistoryData data3{
|
||||
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502902}", .deviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1532}", .zoneIndexSet = { 3 }
|
||||
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502902}", .deviceId = DeviceIdData::ParseDeviceId(L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1532}").value(), .zoneIndexSet = {
|
||||
3 }
|
||||
};
|
||||
AppZoneHistoryData data4{
|
||||
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502903}", .deviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1533}", .zoneIndexSet = { 4 }
|
||||
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502903}", .deviceId = DeviceIdData::ParseDeviceId(L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1533}").value(), .zoneIndexSet = {
|
||||
4 }
|
||||
};
|
||||
zoneHistoryArray.Append(AppZoneHistoryJSON::ToJson(AppZoneHistoryJSON{ L"app-path-1", std::vector<AppZoneHistoryData>{ data1 } }));
|
||||
zoneHistoryArray.Append(AppZoneHistoryJSON::ToJson(AppZoneHistoryJSON{ L"app-path-2", std::vector<AppZoneHistoryData>{ data2 } }));
|
||||
@@ -1238,7 +1252,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
const auto& actual = appZoneHistoryMap.at(expected->appPath);
|
||||
Assert::AreEqual(expected->data.size(), actual.size());
|
||||
Assert::AreEqual(expected->data[0].deviceId.c_str(), actual[0].deviceId.c_str());
|
||||
Assert::IsTrue(expected->data[0].deviceId == actual[0].deviceId);
|
||||
Assert::AreEqual(expected->data[0].zoneSetUuid.c_str(), actual[0].zoneSetUuid.c_str());
|
||||
Assert::IsTrue(expected->data[0].zoneIndexSet == actual[0].zoneIndexSet);
|
||||
|
||||
@@ -1253,19 +1267,23 @@ namespace FancyZonesUnitTests
|
||||
|
||||
const auto appPath = L"app-path";
|
||||
AppZoneHistoryData data1{
|
||||
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502900}", .deviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1530}", .zoneIndexSet = { 1 }
|
||||
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502900}", .deviceId = DeviceIdData::ParseDeviceId(L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1530}").value(), .zoneIndexSet = {
|
||||
1 }
|
||||
};
|
||||
zoneHistoryArray.Append(AppZoneHistoryJSON::ToJson(AppZoneHistoryJSON{ appPath, std::vector<AppZoneHistoryData>{ data1 } }));
|
||||
AppZoneHistoryData data2{
|
||||
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502901}", .deviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1531}", .zoneIndexSet = { 2 }
|
||||
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502901}", .deviceId = DeviceIdData::ParseDeviceId(L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1531}").value(), .zoneIndexSet = {
|
||||
2 }
|
||||
};
|
||||
zoneHistoryArray.Append(AppZoneHistoryJSON::ToJson(AppZoneHistoryJSON{ appPath, std::vector<AppZoneHistoryData>{ data2 } }));
|
||||
AppZoneHistoryData data3{
|
||||
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502902}", .deviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1532}", .zoneIndexSet = { 3 }
|
||||
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502902}", .deviceId = DeviceIdData::ParseDeviceId(L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1532}").value(), .zoneIndexSet = {
|
||||
3 }
|
||||
};
|
||||
zoneHistoryArray.Append(AppZoneHistoryJSON::ToJson(AppZoneHistoryJSON{ appPath, std::vector<AppZoneHistoryData>{ data3 } }));
|
||||
AppZoneHistoryData expected{
|
||||
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502903}", .deviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1533}", .zoneIndexSet = { 4 }
|
||||
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502903}", .deviceId = DeviceIdData::ParseDeviceId(L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1533}").value(), .zoneIndexSet = {
|
||||
4 }
|
||||
};
|
||||
zoneHistoryArray.Append(AppZoneHistoryJSON::ToJson(AppZoneHistoryJSON{ appPath, std::vector<AppZoneHistoryData>{ expected } }));
|
||||
json.SetNamedValue(L"app-zone-history", json::JsonValue::Parse(zoneHistoryArray.Stringify()));
|
||||
@@ -1276,7 +1294,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
const auto& actual = appZoneHistoryMap.at(appPath);
|
||||
Assert::AreEqual((size_t)1, actual.size());
|
||||
Assert::AreEqual(expected.deviceId.c_str(), actual[0].deviceId.c_str());
|
||||
Assert::IsTrue(expected.deviceId == actual[0].deviceId);
|
||||
Assert::AreEqual(expected.zoneSetUuid.c_str(), actual[0].zoneSetUuid.c_str());
|
||||
Assert::IsTrue(expected.zoneIndexSet == actual[0].zoneIndexSet);
|
||||
}
|
||||
@@ -1610,7 +1628,7 @@ namespace FancyZonesUnitTests
|
||||
{
|
||||
FancyZonesData data;
|
||||
data.SetSettingsModulePath(m_moduleName);
|
||||
const std::wstring uniqueId = m_defaultDeviceId;
|
||||
const auto uniqueId = m_defaultDeviceId;
|
||||
|
||||
json::JsonArray devices;
|
||||
devices.Append(m_defaultCustomDeviceValue);
|
||||
@@ -1635,7 +1653,7 @@ namespace FancyZonesUnitTests
|
||||
FancyZonesData data;
|
||||
data.SetSettingsModulePath(m_moduleName);
|
||||
const std::wstring expected = L"{39B25DD2-130D-4B5D-8851-4791D66B1539}";
|
||||
const std::wstring uniqueId = m_defaultDeviceId;
|
||||
const auto uniqueId = m_defaultDeviceId;
|
||||
|
||||
json::JsonArray devices;
|
||||
devices.Append(m_defaultCustomDeviceValue);
|
||||
@@ -1661,7 +1679,8 @@ namespace FancyZonesUnitTests
|
||||
data.SetSettingsModulePath(m_moduleName);
|
||||
|
||||
const std::wstring expected = L"{33A2B101-06E0-437B-A61E-CDBECF502906}";
|
||||
const std::wstring uniqueId = L"id-not-contained-by-device-info-map_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
|
||||
auto uniqueId = m_defaultDeviceId;
|
||||
uniqueId.deviceName = L"id-not-contained-by-device-info-map";
|
||||
|
||||
json::JsonArray devices;
|
||||
devices.Append(m_defaultCustomDeviceValue);
|
||||
@@ -1706,7 +1725,8 @@ namespace FancyZonesUnitTests
|
||||
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502906}", .deviceId = L"device-id", .zoneIndexSet = { 54321 }
|
||||
};
|
||||
AppZoneHistoryJSON appZoneHistory{ L"app-path", std::vector<AppZoneHistoryData>{ data } };
|
||||
DeviceInfoJSON deviceInfo{ L"{33A2B101-06E0-437B-A61E-CDBECF502906}", DeviceInfoData{ ZoneSetData{ L"uuid", ZoneSetLayoutType::Custom }, true, 16, 3 } };
|
||||
|
||||
DeviceInfoJSON deviceInfo { FancyZonesDataTypes::DeviceIdData{ L"device-id", 0, 0, m_defaultVDId }, DeviceInfoData{ ZoneSetData{ L"uuid", ZoneSetLayoutType::Custom }, true, 16, 3 } };
|
||||
LayoutQuickKeyJSON quickKeys{ L"{33A2B101-06E0-437B-A61E-CDBECF502906}", 1 };
|
||||
json::JsonArray zoneSetsArray, appZonesArray, deviceInfoArray, quickKeysArray;
|
||||
zoneSetsArray.Append(CustomZoneSetJSON::ToJson(zoneSets));
|
||||
@@ -1836,7 +1856,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD (AppLastZoneIndex)
|
||||
{
|
||||
const std::wstring deviceId = L"device-id";
|
||||
const FancyZonesDataTypes::DeviceIdData deviceId{ L"device-id" };
|
||||
const std::wstring zoneSetId = L"zoneset-uuid";
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
FancyZonesData data;
|
||||
@@ -1852,7 +1872,7 @@ namespace FancyZonesUnitTests
|
||||
TEST_METHOD (AppLastZoneIndexZero)
|
||||
{
|
||||
const std::wstring zoneSetId = L"zoneset-uuid";
|
||||
const std::wstring deviceId = L"device-id";
|
||||
const FancyZonesDataTypes::DeviceIdData deviceId{ L"device-id" };
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
FancyZonesData data;
|
||||
data.SetSettingsModulePath(m_moduleName);
|
||||
@@ -1865,7 +1885,7 @@ namespace FancyZonesUnitTests
|
||||
TEST_METHOD (AppLastZoneIndexNegative)
|
||||
{
|
||||
const std::wstring zoneSetId = L"zoneset-uuid";
|
||||
const std::wstring deviceId = L"device-id";
|
||||
const FancyZonesDataTypes::DeviceIdData deviceId{ L"device-id" };
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
FancyZonesData data;
|
||||
data.SetSettingsModulePath(m_moduleName);
|
||||
@@ -1878,7 +1898,7 @@ namespace FancyZonesUnitTests
|
||||
TEST_METHOD (AppLastZoneIndexOverflow)
|
||||
{
|
||||
const std::wstring zoneSetId = L"zoneset-uuid";
|
||||
const std::wstring deviceId = L"device-id";
|
||||
const FancyZonesDataTypes::DeviceIdData deviceId{ L"device-id" };
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
FancyZonesData data;
|
||||
data.SetSettingsModulePath(m_moduleName);
|
||||
@@ -1891,7 +1911,7 @@ namespace FancyZonesUnitTests
|
||||
TEST_METHOD (AppLastZoneIndexOverride)
|
||||
{
|
||||
const std::wstring zoneSetId = L"zoneset-uuid";
|
||||
const std::wstring deviceId = L"device-id";
|
||||
const FancyZonesDataTypes::DeviceIdData deviceId{ L"device-id" };
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
FancyZonesData data;
|
||||
data.SetSettingsModulePath(m_moduleName);
|
||||
@@ -1906,7 +1926,7 @@ namespace FancyZonesUnitTests
|
||||
TEST_METHOD (AppLastZoneInvalidWindow)
|
||||
{
|
||||
const std::wstring zoneSetId = L"zoneset-uuid";
|
||||
const std::wstring deviceId = L"device-id";
|
||||
const FancyZonesDataTypes::DeviceIdData deviceId{ L"device-id" };
|
||||
const auto window = Mocks::Window();
|
||||
FancyZonesData data;
|
||||
data.SetSettingsModulePath(m_moduleName);
|
||||
@@ -1920,19 +1940,20 @@ namespace FancyZonesUnitTests
|
||||
TEST_METHOD (AppLastZoneNullWindow)
|
||||
{
|
||||
const std::wstring zoneSetId = L"zoneset-uuid";
|
||||
const FancyZonesDataTypes::DeviceIdData deviceId{ L"device-id" };
|
||||
const auto window = nullptr;
|
||||
FancyZonesData data;
|
||||
data.SetSettingsModulePath(m_moduleName);
|
||||
|
||||
const int expectedZoneIndex = 1;
|
||||
Assert::IsFalse(data.SetAppLastZones(window, L"device-id", zoneSetId, { expectedZoneIndex }));
|
||||
Assert::IsFalse(data.SetAppLastZones(window, deviceId, zoneSetId, { expectedZoneIndex }));
|
||||
}
|
||||
|
||||
TEST_METHOD (AppLastdeviceIdTest)
|
||||
{
|
||||
const std::wstring zoneSetId = L"zoneset-uuid";
|
||||
const std::wstring deviceId1 = L"device-id-1";
|
||||
const std::wstring deviceId2 = L"device-id-2";
|
||||
const FancyZonesDataTypes::DeviceIdData deviceId1{ L"device-id-1" };
|
||||
const FancyZonesDataTypes::DeviceIdData deviceId2{ L"device-id-2" };
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
FancyZonesData data;
|
||||
data.SetSettingsModulePath(m_moduleName);
|
||||
@@ -1947,7 +1968,7 @@ namespace FancyZonesUnitTests
|
||||
{
|
||||
const std::wstring zoneSetId1 = L"zoneset-uuid-1";
|
||||
const std::wstring zoneSetId2 = L"zoneset-uuid-2";
|
||||
const std::wstring deviceId = L"device-id";
|
||||
const FancyZonesDataTypes::DeviceIdData deviceId{ L"device-id" };
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
FancyZonesData data;
|
||||
data.SetSettingsModulePath(m_moduleName);
|
||||
@@ -1961,7 +1982,7 @@ namespace FancyZonesUnitTests
|
||||
TEST_METHOD (AppLastZoneRemoveWindow)
|
||||
{
|
||||
const std::wstring zoneSetId = L"zoneset-uuid";
|
||||
const std::wstring deviceId = L"device-id";
|
||||
const FancyZonesDataTypes::DeviceIdData deviceId{ L"device-id" };
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
FancyZonesData data;
|
||||
data.SetSettingsModulePath(m_moduleName);
|
||||
@@ -1974,7 +1995,7 @@ namespace FancyZonesUnitTests
|
||||
TEST_METHOD (AppLastZoneRemoveUnknownWindow)
|
||||
{
|
||||
const std::wstring zoneSetId = L"zoneset-uuid";
|
||||
const std::wstring deviceId = L"device-id";
|
||||
const FancyZonesDataTypes::DeviceIdData deviceId{ L"device-id" };
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
FancyZonesData data;
|
||||
data.SetSettingsModulePath(m_moduleName);
|
||||
@@ -1987,7 +2008,7 @@ namespace FancyZonesUnitTests
|
||||
{
|
||||
const std::wstring zoneSetIdToInsert = L"zoneset-uuid-to-insert";
|
||||
const std::wstring zoneSetIdToRemove = L"zoneset-uuid-to-remove";
|
||||
const std::wstring deviceId = L"device-id";
|
||||
const FancyZonesDataTypes::DeviceIdData deviceId{ L"device-id" };
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
FancyZonesData data;
|
||||
data.SetSettingsModulePath(m_moduleName);
|
||||
@@ -2000,8 +2021,8 @@ namespace FancyZonesUnitTests
|
||||
TEST_METHOD (AppLastZoneRemoveUnknownWindowId)
|
||||
{
|
||||
const std::wstring zoneSetId = L"zoneset-uuid";
|
||||
const std::wstring deviceIdToInsert = L"device-id-insert";
|
||||
const std::wstring deviceIdToRemove = L"device-id-remove";
|
||||
const FancyZonesDataTypes::DeviceIdData deviceIdToInsert{ L"device-id-insert" };
|
||||
const FancyZonesDataTypes::DeviceIdData deviceIdToRemove{ L"device-id-remove" };
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
FancyZonesData data;
|
||||
data.SetSettingsModulePath(m_moduleName);
|
||||
@@ -2014,7 +2035,7 @@ namespace FancyZonesUnitTests
|
||||
TEST_METHOD (AppLastZoneRemoveNullWindow)
|
||||
{
|
||||
const std::wstring zoneSetId = L"zoneset-uuid";
|
||||
const std::wstring deviceId = L"device-id";
|
||||
const FancyZonesDataTypes::DeviceIdData deviceId{ L"device-id" };
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
FancyZonesData data;
|
||||
data.SetSettingsModulePath(m_moduleName);
|
||||
|
||||
@@ -74,15 +74,15 @@
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
<Import Project="..\..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="..\..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -71,7 +71,7 @@ namespace FancyZonesUnitTests
|
||||
CLSIDFromString(expectedGuidStr, &guid);
|
||||
const FancyZonesDataTypes::DeviceIdData expected{ L"AOC0001#5&37ac4db&0&UID160002", 1536, 960, guid };
|
||||
|
||||
const auto actual = ParseDeviceId(input);
|
||||
const auto actual = FancyZonesDataTypes::DeviceIdData::ParseDeviceId(input);
|
||||
Assert::IsTrue(actual.has_value());
|
||||
|
||||
Assert::AreEqual(expected.deviceName, actual->deviceName);
|
||||
@@ -93,7 +93,7 @@ namespace FancyZonesUnitTests
|
||||
CLSIDFromString(expectedGuidStr, &guid);
|
||||
const FancyZonesDataTypes::DeviceIdData expected{ L"AOC0001#5&37ac4db&0&UID160002", 1536, 960, guid, L"monitorId" };
|
||||
|
||||
const auto actual = ParseDeviceId(input);
|
||||
const auto actual = FancyZonesDataTypes::DeviceIdData::ParseDeviceId(input);
|
||||
Assert::IsTrue(actual.has_value());
|
||||
|
||||
Assert::AreEqual(expected.deviceName, actual->deviceName);
|
||||
@@ -116,7 +116,7 @@ namespace FancyZonesUnitTests
|
||||
CLSIDFromString(expectedGuidStr, &guid);
|
||||
const FancyZonesDataTypes::DeviceIdData expected{ L"AOC00015&37ac4db&0&UID160002", 1536, 960, guid };
|
||||
|
||||
const auto actual = ParseDeviceId(input);
|
||||
const auto actual = FancyZonesDataTypes::DeviceIdData::ParseDeviceId(input);
|
||||
Assert::IsTrue(actual.has_value());
|
||||
|
||||
Assert::AreEqual(expected.deviceName, actual->deviceName);
|
||||
@@ -133,7 +133,7 @@ namespace FancyZonesUnitTests
|
||||
{
|
||||
// no width or height
|
||||
const std::wstring input = L"AOC00015&37ac4db&0&UID160002_1536960_{E0A2904E-889C-4532-95B1-28FE15C16F66}";
|
||||
const auto actual = ParseDeviceId(input);
|
||||
const auto actual = FancyZonesDataTypes::DeviceIdData::ParseDeviceId(input);
|
||||
Assert::IsFalse(actual.has_value());
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ namespace FancyZonesUnitTests
|
||||
{
|
||||
// no width and height
|
||||
const std::wstring input = L"AOC00015&37ac4db&0&UID160002_{E0A2904E-889C-4532-95B1-28FE15C16F66}_monitorId";
|
||||
const auto actual = ParseDeviceId(input);
|
||||
const auto actual = FancyZonesDataTypes::DeviceIdData::ParseDeviceId(input);
|
||||
Assert::IsFalse(actual.has_value());
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ namespace FancyZonesUnitTests
|
||||
{
|
||||
// no guid
|
||||
const std::wstring input = L"AOC00015&37ac4db&0&UID160002_1536960_";
|
||||
const auto actual = ParseDeviceId(input);
|
||||
const auto actual = FancyZonesDataTypes::DeviceIdData::ParseDeviceId(input);
|
||||
Assert::IsFalse(actual.has_value());
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ namespace FancyZonesUnitTests
|
||||
{
|
||||
// invalid guid
|
||||
const std::wstring input = L"AOC00015&37ac4db&0&UID160002_1536960_{asdf}";
|
||||
const auto actual = ParseDeviceId(input);
|
||||
const auto actual = FancyZonesDataTypes::DeviceIdData::ParseDeviceId(input);
|
||||
Assert::IsFalse(actual.has_value());
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ namespace FancyZonesUnitTests
|
||||
{
|
||||
// invalid width/height
|
||||
const std::wstring input = L"AOC00015&37ac4db&0&UID160002_15a6_960_{E0A2904E-889C-4532-95B1-28FE15C16F66}";
|
||||
const auto actual = ParseDeviceId(input);
|
||||
const auto actual = FancyZonesDataTypes::DeviceIdData::ParseDeviceId(input);
|
||||
Assert::IsFalse(actual.has_value());
|
||||
}
|
||||
|
||||
@@ -173,7 +173,7 @@ namespace FancyZonesUnitTests
|
||||
{
|
||||
// changed order
|
||||
const std::wstring input = L"AOC00015&37ac4db&0&UID160002_15a6_960_monitorId_{E0A2904E-889C-4532-95B1-28FE15C16F66}";
|
||||
const auto actual = ParseDeviceId(input);
|
||||
const auto actual = FancyZonesDataTypes::DeviceIdData::ParseDeviceId(input);
|
||||
Assert::IsFalse(actual.has_value());
|
||||
}
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_CLASS (WorkAreaCreationUnitTests)
|
||||
{
|
||||
std::wstringstream m_parentUniqueId;
|
||||
std::wstringstream m_uniqueId;
|
||||
FancyZonesDataTypes::DeviceIdData m_parentUniqueId;
|
||||
FancyZonesDataTypes::DeviceIdData m_uniqueId;
|
||||
|
||||
HINSTANCE m_hInst{};
|
||||
HMONITOR m_monitor{};
|
||||
@@ -40,7 +40,7 @@ namespace FancyZonesUnitTests
|
||||
const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom);
|
||||
|
||||
Assert::IsNotNull(workArea.get());
|
||||
Assert::AreEqual(m_uniqueId.str().c_str(), workArea->UniqueId().c_str());
|
||||
Assert::IsTrue(m_uniqueId == workArea->UniqueId());
|
||||
}
|
||||
|
||||
TEST_METHOD_INITIALIZE(Init)
|
||||
@@ -51,9 +51,16 @@ namespace FancyZonesUnitTests
|
||||
m_monitorInfo.cbSize = sizeof(m_monitorInfo);
|
||||
Assert::AreNotEqual(0, GetMonitorInfoW(m_monitor, &m_monitorInfo));
|
||||
|
||||
m_parentUniqueId << L"DELA026#5&10a58c63&0&UID16777488_" << m_monitorInfo.rcMonitor.right << "_" << m_monitorInfo.rcMonitor.bottom << "_{61FA9FC0-26A6-4B37-A834-491C148DFC57}";
|
||||
m_uniqueId << L"DELA026#5&10a58c63&0&UID16777488_" << m_monitorInfo.rcMonitor.right << "_" << m_monitorInfo.rcMonitor.bottom << "_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
|
||||
|
||||
m_parentUniqueId.deviceName = L"DELA026#5&10a58c63&0&UID16777488";
|
||||
m_parentUniqueId.width = m_monitorInfo.rcMonitor.right - m_monitorInfo.rcMonitor.left;
|
||||
m_parentUniqueId.height = m_monitorInfo.rcMonitor.bottom - m_monitorInfo.rcMonitor.top;
|
||||
CLSIDFromString(L"{61FA9FC0-26A6-4B37-A834-491C148DFC57}", &m_parentUniqueId.virtualDesktopId);
|
||||
|
||||
m_uniqueId.deviceName = L"DELA026#5&10a58c63&0&UID16777488";
|
||||
m_uniqueId.width = m_monitorInfo.rcMonitor.right - m_monitorInfo.rcMonitor.left;
|
||||
m_uniqueId.height = m_monitorInfo.rcMonitor.bottom - m_monitorInfo.rcMonitor.top;
|
||||
CLSIDFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}", &m_uniqueId.virtualDesktopId);
|
||||
|
||||
m_fancyZonesData.SetSettingsModulePath(L"FancyZonesUnitTests");
|
||||
m_fancyZonesData.clear_data();
|
||||
|
||||
@@ -71,7 +78,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD (CreateWorkArea)
|
||||
{
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
testWorkArea(workArea);
|
||||
|
||||
auto* activeZoneSet{ workArea->ActiveZoneSet() };
|
||||
@@ -82,7 +89,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD (CreateWorkAreaNoHinst)
|
||||
{
|
||||
auto workArea = MakeWorkArea({}, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
auto workArea = MakeWorkArea({}, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
testWorkArea(workArea);
|
||||
|
||||
auto* activeZoneSet{ workArea->ActiveZoneSet() };
|
||||
@@ -93,7 +100,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD (CreateWorkAreaNoHinstFlashZones)
|
||||
{
|
||||
auto workArea = MakeWorkArea({}, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
auto workArea = MakeWorkArea({}, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
testWorkArea(workArea);
|
||||
|
||||
auto* activeZoneSet{ workArea->ActiveZoneSet() };
|
||||
@@ -104,21 +111,32 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD (CreateWorkAreaNoMonitor)
|
||||
{
|
||||
auto workArea = MakeWorkArea(m_hInst, {}, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
auto workArea = MakeWorkArea(m_hInst, {}, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
testWorkArea(workArea);
|
||||
}
|
||||
|
||||
TEST_METHOD (CreateWorkAreaNoDeviceId)
|
||||
{
|
||||
// Generate unique id without device id
|
||||
std::wstring uniqueId = FancyZonesUtils::GenerateUniqueId(m_monitor, {}, m_virtualDesktopId);
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
FancyZonesDataTypes::DeviceIdData uniqueIdData;
|
||||
uniqueIdData.virtualDesktopId = m_virtualDesktopGuid;
|
||||
|
||||
MONITORINFOEXW mi;
|
||||
mi.cbSize = sizeof(mi);
|
||||
if (GetMonitorInfo(m_monitor, &mi))
|
||||
{
|
||||
FancyZonesUtils::Rect const monitorRect(mi.rcMonitor);
|
||||
uniqueIdData.width = monitorRect.width();
|
||||
uniqueIdData.height = monitorRect.height();
|
||||
}
|
||||
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, uniqueIdData, {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
|
||||
const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom);
|
||||
const std::wstring expectedUniqueId = L"FallbackDevice_" + std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom) + L"_" + m_virtualDesktopId;
|
||||
const FancyZonesDataTypes::DeviceIdData expectedUniqueId{ L"FallbackDevice", m_monitorInfo.rcMonitor.right - m_monitorInfo.rcMonitor.left, m_monitorInfo.rcMonitor.bottom - m_monitorInfo.rcMonitor.top, m_virtualDesktopGuid };
|
||||
|
||||
Assert::IsNotNull(workArea.get());
|
||||
Assert::AreEqual(expectedUniqueId.c_str(), workArea->UniqueId().c_str());
|
||||
Assert::IsTrue(expectedUniqueId == workArea->UniqueId());
|
||||
|
||||
auto* activeZoneSet{ workArea->ActiveZoneSet() };
|
||||
Assert::IsNotNull(activeZoneSet);
|
||||
@@ -129,12 +147,22 @@ namespace FancyZonesUnitTests
|
||||
TEST_METHOD (CreateWorkAreaNoDesktopId)
|
||||
{
|
||||
// Generate unique id without virtual desktop id
|
||||
std::wstring uniqueId = FancyZonesUtils::GenerateUniqueId(m_monitor, m_deviceId, {});
|
||||
FancyZonesDataTypes::DeviceIdData uniqueId;
|
||||
uniqueId.deviceName = FancyZonesUtils::TrimDeviceId(m_deviceId);
|
||||
|
||||
MONITORINFOEXW mi;
|
||||
mi.cbSize = sizeof(mi);
|
||||
if (GetMonitorInfo(m_monitor, &mi))
|
||||
{
|
||||
FancyZonesUtils::Rect const monitorRect(mi.rcMonitor);
|
||||
uniqueId.width = monitorRect.width();
|
||||
uniqueId.height = monitorRect.height();
|
||||
}
|
||||
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
|
||||
const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom);
|
||||
Assert::IsNotNull(workArea.get());
|
||||
Assert::IsTrue(workArea->UniqueId().empty());
|
||||
|
||||
auto* activeZoneSet{ workArea->ActiveZoneSet() };
|
||||
Assert::IsNotNull(activeZoneSet);
|
||||
@@ -152,17 +180,17 @@ namespace FancyZonesUnitTests
|
||||
const auto customSetGuid = Helpers::CreateGuidString();
|
||||
const auto parentZoneSet = ZoneSetData{ customSetGuid, type };
|
||||
const auto parentDeviceInfo = DeviceInfoData{ parentZoneSet, true, spacing, zoneCount };
|
||||
m_fancyZonesData.SetDeviceInfo(m_parentUniqueId.str(), parentDeviceInfo);
|
||||
m_fancyZonesData.SetDeviceInfo(m_parentUniqueId, parentDeviceInfo);
|
||||
|
||||
auto parentWorkArea = MakeWorkArea(m_hInst, m_monitor, m_parentUniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
auto parentWorkArea = MakeWorkArea(m_hInst, m_monitor, m_parentUniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
|
||||
// newWorkArea = false - workArea won't be cloned from parent
|
||||
auto actualWorkArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
auto actualWorkArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
|
||||
Assert::IsNotNull(actualWorkArea->ActiveZoneSet());
|
||||
|
||||
Assert::IsTrue(m_fancyZonesData.GetDeviceInfoMap().contains(m_uniqueId.str()));
|
||||
auto currentDeviceInfo = m_fancyZonesData.GetDeviceInfoMap().at(m_uniqueId.str());
|
||||
Assert::IsTrue(m_fancyZonesData.GetDeviceInfoMap().contains(m_uniqueId));
|
||||
auto currentDeviceInfo = m_fancyZonesData.GetDeviceInfoMap().at(m_uniqueId);
|
||||
// default values
|
||||
Assert::AreEqual(true, currentDeviceInfo.showSpacing);
|
||||
Assert::AreEqual(3, currentDeviceInfo.zoneCount);
|
||||
@@ -173,7 +201,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_CLASS (WorkAreaUnitTests)
|
||||
{
|
||||
std::wstringstream m_uniqueId;
|
||||
FancyZonesDataTypes::DeviceIdData m_uniqueId;
|
||||
|
||||
HINSTANCE m_hInst{};
|
||||
HMONITOR m_monitor{};
|
||||
@@ -191,8 +219,11 @@ namespace FancyZonesUnitTests
|
||||
m_monitorInfo.cbSize = sizeof(m_monitorInfo);
|
||||
Assert::AreNotEqual(0, GetMonitorInfoW(m_monitor, &m_monitorInfo));
|
||||
|
||||
m_uniqueId << L"DELA026#5&10a58c63&0&UID16777488_" << m_monitorInfo.rcMonitor.right << "_" << m_monitorInfo.rcMonitor.bottom << "_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
|
||||
|
||||
m_uniqueId.deviceName = L"DELA026#5&10a58c63&0&UID16777488";
|
||||
m_uniqueId.width = m_monitorInfo.rcMonitor.right - m_monitorInfo.rcMonitor.left;
|
||||
m_uniqueId.height = m_monitorInfo.rcMonitor.bottom - m_monitorInfo.rcMonitor.top;
|
||||
CLSIDFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}", &m_uniqueId.virtualDesktopId);
|
||||
|
||||
m_fancyZonesData.SetSettingsModulePath(L"FancyZonesUnitTests");
|
||||
m_fancyZonesData.clear_data();
|
||||
|
||||
@@ -207,7 +238,7 @@ namespace FancyZonesUnitTests
|
||||
public:
|
||||
TEST_METHOD (MoveSizeEnter)
|
||||
{
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
|
||||
const auto expected = S_OK;
|
||||
const auto actual = workArea->MoveSizeEnter(Mocks::Window());
|
||||
@@ -217,7 +248,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD (MoveSizeEnterTwice)
|
||||
{
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
|
||||
const auto expected = S_OK;
|
||||
|
||||
@@ -229,7 +260,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD (MoveSizeUpdate)
|
||||
{
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
|
||||
const auto expected = S_OK;
|
||||
const auto actual = workArea->MoveSizeUpdate(POINT{ 0, 0 }, true, false);
|
||||
@@ -239,7 +270,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD (MoveSizeUpdatePointNegativeCoordinates)
|
||||
{
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
|
||||
const auto expected = S_OK;
|
||||
const auto actual = workArea->MoveSizeUpdate(POINT{ -10, -10 }, true, false);
|
||||
@@ -249,7 +280,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD (MoveSizeUpdatePointBigCoordinates)
|
||||
{
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
|
||||
const auto expected = S_OK;
|
||||
const auto actual = workArea->MoveSizeUpdate(POINT{ m_monitorInfo.rcMonitor.right + 1, m_monitorInfo.rcMonitor.bottom + 1 }, true, false);
|
||||
@@ -259,7 +290,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD (MoveSizeEnd)
|
||||
{
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
|
||||
const auto window = Mocks::Window();
|
||||
workArea->MoveSizeEnter(window);
|
||||
@@ -276,7 +307,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD (MoveSizeEndWindowNotAdded)
|
||||
{
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
|
||||
const auto window = Mocks::Window();
|
||||
workArea->MoveSizeEnter(window);
|
||||
@@ -292,7 +323,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD (MoveSizeEndDifferentWindows)
|
||||
{
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
|
||||
const auto window = Mocks::Window();
|
||||
workArea->MoveSizeEnter(window);
|
||||
@@ -305,7 +336,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD (MoveSizeEndWindowNotSet)
|
||||
{
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
|
||||
const auto expected = E_INVALIDARG;
|
||||
const auto actual = workArea->MoveSizeEnd(Mocks::Window(), POINT{ 0, 0 });
|
||||
@@ -315,7 +346,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD (MoveSizeEndInvalidPoint)
|
||||
{
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
|
||||
const auto window = Mocks::Window();
|
||||
workArea->MoveSizeEnter(window);
|
||||
@@ -332,7 +363,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD (MoveWindowIntoZoneByIndex)
|
||||
{
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
Assert::IsNotNull(workArea->ActiveZoneSet());
|
||||
|
||||
workArea->MoveWindowIntoZoneByIndex(Mocks::Window(), 0);
|
||||
@@ -342,7 +373,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD (MoveWindowIntoZoneByDirectionAndIndex)
|
||||
{
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
Assert::IsNotNull(workArea->ActiveZoneSet());
|
||||
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
@@ -357,7 +388,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD (MoveWindowIntoZoneByDirectionManyTimes)
|
||||
{
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
Assert::IsNotNull(workArea->ActiveZoneSet());
|
||||
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
@@ -374,7 +405,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD (SaveWindowProcessToZoneIndexNullptrWindow)
|
||||
{
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
Assert::IsNotNull(workArea->ActiveZoneSet());
|
||||
|
||||
workArea->SaveWindowProcessToZoneIndex(nullptr);
|
||||
@@ -385,7 +416,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD (SaveWindowProcessToZoneIndexNoWindowAdded)
|
||||
{
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
Assert::IsNotNull(workArea->ActiveZoneSet());
|
||||
|
||||
auto window = Mocks::WindowCreate(m_hInst);
|
||||
@@ -400,7 +431,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD (SaveWindowProcessToZoneIndexNoWindowAddedWithFilledAppZoneHistory)
|
||||
{
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
Assert::IsNotNull(workArea->ActiveZoneSet());
|
||||
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
@@ -428,7 +459,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD (SaveWindowProcessToZoneIndexWindowAdded)
|
||||
{
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
Assert::IsNotNull(workArea->ActiveZoneSet());
|
||||
|
||||
auto window = Mocks::WindowCreate(m_hInst);
|
||||
@@ -458,7 +489,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD (WhenWindowIsNotResizablePlacingItIntoTheZoneShouldNotResizeIt)
|
||||
{
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
|
||||
Assert::IsNotNull(workArea->ActiveZoneSet());
|
||||
|
||||
auto window = Mocks::WindowCreate(m_hInst);
|
||||
|
||||
@@ -1046,8 +1046,7 @@ namespace FancyZonesUnitTests
|
||||
TEST_METHOD (CustomZoneFromValidCanvasLayoutInfo)
|
||||
{
|
||||
//prepare device data
|
||||
const std::wstring zoneUuid = L"default_device_id";
|
||||
FancyZonesDataInstance().SetDeviceInfo(zoneUuid, DeviceInfoData{ ZoneSetData{ L"uuid", ZoneSetLayoutType::Custom }, true, 16, 3 });
|
||||
FancyZonesDataInstance().SetDeviceInfo(FancyZonesDataTypes::DeviceIdData{ L"default_device_id" }, DeviceInfoData{ ZoneSetData{ L"uuid", ZoneSetLayoutType::Custom }, true, 16, 3 });
|
||||
|
||||
//prepare expected data
|
||||
wil::unique_cotaskmem_string uuid;
|
||||
@@ -1072,8 +1071,7 @@ namespace FancyZonesUnitTests
|
||||
TEST_METHOD (CustomZoneFromValidGridFullLayoutInfo)
|
||||
{
|
||||
//prepare device data
|
||||
const std::wstring zoneUuid = L"default_device_id";
|
||||
FancyZonesDataInstance().SetDeviceInfo(zoneUuid, DeviceInfoData{ ZoneSetData{ L"uuid", ZoneSetLayoutType::Custom }, true, 16, 3 });
|
||||
FancyZonesDataInstance().SetDeviceInfo(FancyZonesDataTypes::DeviceIdData{ L"default_device_id" }, DeviceInfoData{ ZoneSetData{ L"uuid", ZoneSetLayoutType::Custom }, true, 16, 3 });
|
||||
|
||||
//prepare expected data
|
||||
wil::unique_cotaskmem_string uuid;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.200729.8" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.200902.2" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.210204.1" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -12,6 +12,7 @@ using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using FancyZonesEditor.Utils;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Common.UI;
|
||||
@@ -178,6 +179,11 @@ namespace FancyZonesEditor
|
||||
{
|
||||
MainWindowSettings.IsShiftKeyPressed = true;
|
||||
}
|
||||
else if (e.Key == Key.Tab && (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)))
|
||||
{
|
||||
e.Handled = true;
|
||||
App.Overlay.FocusEditor();
|
||||
}
|
||||
}
|
||||
|
||||
public static void ShowExceptionMessageBox(string message, Exception exception = null)
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Input;
|
||||
using FancyZonesEditor.Models;
|
||||
|
||||
@@ -38,9 +40,20 @@ namespace FancyZonesEditor
|
||||
InitializeComponent();
|
||||
Loaded += GridEditor_Loaded;
|
||||
Unloaded += GridEditor_Unloaded;
|
||||
KeyDown += GridEditor_KeyDown;
|
||||
KeyUp += GridEditor_KeyUp;
|
||||
gridEditorUniqueId = ++gridEditorUniqueIdCounter;
|
||||
}
|
||||
|
||||
public void FocusZone()
|
||||
{
|
||||
if (Preview.Children.Count > 0)
|
||||
{
|
||||
var zone = Preview.Children[0] as GridZone;
|
||||
zone.Focus();
|
||||
}
|
||||
}
|
||||
|
||||
private void GridEditor_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
((App)Application.Current).MainWindowSettings.PropertyChanged += ZoneSettings_PropertyChanged;
|
||||
@@ -58,6 +71,134 @@ namespace FancyZonesEditor
|
||||
SetupUI();
|
||||
}
|
||||
|
||||
private void HandleResizerKeyDown(GridResizer resizer, KeyEventArgs e)
|
||||
{
|
||||
DragDeltaEventArgs args = null;
|
||||
if (resizer.Orientation == Orientation.Horizontal)
|
||||
{
|
||||
if (e.Key == Key.Up)
|
||||
{
|
||||
args = new DragDeltaEventArgs(0, -1);
|
||||
}
|
||||
else if (e.Key == Key.Down)
|
||||
{
|
||||
args = new DragDeltaEventArgs(0, 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (e.Key == Key.Left)
|
||||
{
|
||||
args = new DragDeltaEventArgs(-1, 0);
|
||||
}
|
||||
else if (e.Key == Key.Right)
|
||||
{
|
||||
args = new DragDeltaEventArgs(1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (args != null)
|
||||
{
|
||||
e.Handled = true;
|
||||
Resizer_DragDelta(resizer, args);
|
||||
}
|
||||
|
||||
if (e.Key == Key.Delete)
|
||||
{
|
||||
int resizerIndex = AdornerLayer.Children.IndexOf(resizer);
|
||||
var resizerData = _data.Resizers[resizerIndex];
|
||||
|
||||
var indices = new List<int>(resizerData.PositiveSideIndices);
|
||||
indices.AddRange(resizerData.NegativeSideIndices);
|
||||
_data.DoMerge(indices);
|
||||
SetupUI();
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleResizerKeyUp(GridResizer resizer, KeyEventArgs e)
|
||||
{
|
||||
if (resizer.Orientation == Orientation.Horizontal)
|
||||
{
|
||||
e.Handled = e.Key == Key.Up || e.Key == Key.Down;
|
||||
}
|
||||
else
|
||||
{
|
||||
e.Handled = e.Key == Key.Left || e.Key == Key.Right;
|
||||
}
|
||||
|
||||
if (e.Handled)
|
||||
{
|
||||
int resizerIndex = AdornerLayer.Children.IndexOf(resizer);
|
||||
Resizer_DragCompleted(resizer, null);
|
||||
Debug.Assert(AdornerLayer.Children.Count > resizerIndex, "Resizer index out of range");
|
||||
Keyboard.Focus(AdornerLayer.Children[resizerIndex]);
|
||||
_dragY = _dragX = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleGridZoneKeyUp(GridZone gridZone, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key != Key.S)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Orientation orient = Orientation.Horizontal;
|
||||
int offset = 0;
|
||||
|
||||
int zoneIndex = Preview.Children.IndexOf(gridZone);
|
||||
var zone = _data.Zones[zoneIndex];
|
||||
Debug.Assert(Preview.Children.Count > zoneIndex, "Zone index out of range");
|
||||
|
||||
if (((App)Application.Current).MainWindowSettings.IsShiftKeyPressed)
|
||||
{
|
||||
orient = Orientation.Vertical;
|
||||
offset = gridZone.SnapAtHalfX();
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = gridZone.SnapAtHalfY();
|
||||
}
|
||||
|
||||
gridZone.DoSplit(orient, offset);
|
||||
}
|
||||
|
||||
private void GridEditor_KeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.Tab && (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)))
|
||||
{
|
||||
e.Handled = true;
|
||||
App.Overlay.FocusEditorWindow();
|
||||
}
|
||||
else
|
||||
{
|
||||
var resizer = Keyboard.FocusedElement as GridResizer;
|
||||
if (resizer != null)
|
||||
{
|
||||
HandleResizerKeyDown(resizer, e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void GridEditor_KeyUp(object sender, KeyEventArgs e)
|
||||
{
|
||||
var resizer = Keyboard.FocusedElement as GridResizer;
|
||||
if (resizer != null)
|
||||
{
|
||||
HandleResizerKeyUp(resizer, e);
|
||||
return;
|
||||
}
|
||||
|
||||
var gridZone = Keyboard.FocusedElement as GridZone;
|
||||
if (gridZone != null)
|
||||
{
|
||||
HandleGridZoneKeyUp(gridZone, e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void GridEditor_Unloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
((App)Application.Current).MainWindowSettings.PropertyChanged -= ZoneSettings_PropertyChanged;
|
||||
@@ -267,7 +408,7 @@ namespace FancyZonesEditor
|
||||
delta = Convert.ToInt32(_dragY / actualSize.Height * GridData.Multiplier);
|
||||
}
|
||||
|
||||
if (_data.CanDrag(resizerIndex, delta))
|
||||
if (resizerIndex != -1 && _data.CanDrag(resizerIndex, delta))
|
||||
{
|
||||
// Just update the UI, don't tell _data
|
||||
if (resizer.Orientation == Orientation.Vertical)
|
||||
@@ -328,6 +469,12 @@ namespace FancyZonesEditor
|
||||
{
|
||||
GridResizer resizer = (GridResizer)sender;
|
||||
int resizerIndex = AdornerLayer.Children.IndexOf(resizer);
|
||||
if (resizerIndex == -1)
|
||||
{
|
||||
// Resizer was removed during drag
|
||||
return;
|
||||
}
|
||||
|
||||
Size actualSize = WorkAreaSize();
|
||||
|
||||
double pixelDelta = resizer.Orientation == Orientation.Vertical ?
|
||||
|
||||
@@ -8,19 +8,18 @@
|
||||
xmlns:props="clr-namespace:FancyZonesEditor.Properties"
|
||||
mc:Ignorable="d"
|
||||
Title=""
|
||||
Height="176"
|
||||
MinWidth="360"
|
||||
BorderThickness="0"
|
||||
xmlns:ui="http://schemas.modernwpf.com/2019"
|
||||
ui:WindowHelper.UseModernWindowStyle="True"
|
||||
ui:TitleBar.IsIconVisible="False"
|
||||
SizeToContent="Width"
|
||||
SizeToContent="WidthAndHeight"
|
||||
Background="{DynamicResource PrimaryBackgroundBrush}"
|
||||
ResizeMode="NoResize"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
ContentRendered="EditorWindow_ContentRendered"
|
||||
Closed="OnClosed">
|
||||
<Grid Height="140">
|
||||
<Grid>
|
||||
<Grid
|
||||
Height="36"
|
||||
Background="{DynamicResource SecondaryBackgroundBrush}"
|
||||
@@ -53,8 +52,16 @@
|
||||
Text="{x:Static props:Resources.MergeName}" />
|
||||
<Run Text="{x:Static props:Resources.MergeDescription}" />
|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
Margin="0,8,0,0"
|
||||
TextWrapping="Wrap">
|
||||
<Run
|
||||
FontWeight="Bold"
|
||||
Text="{x:Static props:Resources.KeyboardControlsName}" />
|
||||
<Run Text="{x:Static props:Resources.KeyboardControlsDescription}" />
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<Grid Margin="0,24,0,-4">
|
||||
<Grid Margin="0,24,0,-36">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:FancyZonesEditor"
|
||||
mc:Ignorable="d"
|
||||
Focusable="True"
|
||||
d:DesignHeight="300" d:DesignWidth="300">
|
||||
<Thumb.Template>
|
||||
<ControlTemplate>
|
||||
@@ -35,6 +36,10 @@
|
||||
TargetName="Body"
|
||||
Value="{DynamicResource SystemAccentColorLight1Brush}"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsKeyboardFocused" Value="True">
|
||||
<Setter Property="Background" TargetName="Body"
|
||||
Value="{DynamicResource SystemAccentColorLight3Brush}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Thumb.Template>
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
BorderBrush="{DynamicResource SystemControlBackgroundAccentBrush}"
|
||||
BorderThickness="1"
|
||||
Opacity="1"
|
||||
Focusable="True"
|
||||
IsTabStop="True"
|
||||
ui:ControlHelper.CornerRadius="4"
|
||||
mc:Ignorable="d">
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace FancyZonesEditor
|
||||
private const string GridZoneBackgroundBrushID = "GridZoneBackgroundBrush";
|
||||
private const string SecondaryForegroundBrushID = "SecondaryForegroundBrush";
|
||||
private const string AccentColorBrushID = "SystemControlBackgroundAccentBrush";
|
||||
private const string CanvasCanvasZoneBorderBrushID = "CanvasCanvasZoneBorderBrush";
|
||||
|
||||
public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.Register(ObjectDependencyID, typeof(bool), typeof(GridZone), new PropertyMetadata(false, OnSelectionChanged));
|
||||
|
||||
@@ -75,12 +76,39 @@ namespace FancyZonesEditor
|
||||
|
||||
SizeChanged += GridZone_SizeChanged;
|
||||
|
||||
GotKeyboardFocus += GridZone_GotKeyboardFocus;
|
||||
LostKeyboardFocus += GridZone_LostKeyboardFocus;
|
||||
|
||||
_snapX = snapX;
|
||||
_snapY = snapY;
|
||||
_canSplit = canSplit;
|
||||
_zone = zone;
|
||||
}
|
||||
|
||||
public int SnapAtHalfX()
|
||||
{
|
||||
var half = (_zone.Right - _zone.Left) / 2;
|
||||
var pixelX = _snapX.DataToPixelWithoutSnapping(_zone.Left + half);
|
||||
return _snapX.PixelToDataWithSnapping(pixelX, _zone.Left, _zone.Right);
|
||||
}
|
||||
|
||||
public int SnapAtHalfY()
|
||||
{
|
||||
var half = (_zone.Bottom - _zone.Top) / 2;
|
||||
var pixelY = _snapY.DataToPixelWithoutSnapping(_zone.Top + half);
|
||||
return _snapY.PixelToDataWithSnapping(pixelY, _zone.Top, _zone.Bottom);
|
||||
}
|
||||
|
||||
private void GridZone_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
|
||||
{
|
||||
Opacity = 1;
|
||||
}
|
||||
|
||||
private void GridZone_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
|
||||
{
|
||||
Opacity = 0.5;
|
||||
}
|
||||
|
||||
private void GridZone_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
// using current culture as this is end user facing
|
||||
@@ -241,7 +269,7 @@ namespace FancyZonesEditor
|
||||
MergeComplete?.Invoke(this, e);
|
||||
}
|
||||
|
||||
private void DoSplit(Orientation orientation, int offset)
|
||||
public void DoSplit(Orientation orientation, int offset)
|
||||
{
|
||||
Split?.Invoke(this, new SplitEventArgs(orientation, offset));
|
||||
}
|
||||
|
||||
@@ -419,6 +419,11 @@ namespace FancyZonesEditor
|
||||
{
|
||||
LayoutModel model = element.DataContext as LayoutModel;
|
||||
|
||||
if (_backup != null && model.Guid == _backup.Guid)
|
||||
{
|
||||
_backup = null;
|
||||
}
|
||||
|
||||
if (model == _settings.AppliedModel)
|
||||
{
|
||||
_settings.SetAppliedModel(_settings.BlankModel);
|
||||
|
||||
@@ -253,10 +253,19 @@ namespace FancyZonesEditor
|
||||
|
||||
public void FocusEditor()
|
||||
{
|
||||
if (_editorLayout != null && _editorLayout is CanvasEditor canvasEditor)
|
||||
if (_editorLayout == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_editorLayout is CanvasEditor canvasEditor)
|
||||
{
|
||||
canvasEditor.FocusZone();
|
||||
}
|
||||
else if (_editorLayout is GridEditor gridEditor)
|
||||
{
|
||||
gridEditor.FocusZone();
|
||||
}
|
||||
}
|
||||
|
||||
public void FocusEditorWindow()
|
||||
|
||||
@@ -447,6 +447,29 @@ namespace FancyZonesEditor.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to
|
||||
/// - [Shift]+S to split currently focused zone.
|
||||
/// - Ctrl+Tab to focus zones/resizers.
|
||||
/// - Tab to cycle zones and resizers.
|
||||
/// - Delete to remove the focused resizer.
|
||||
/// - Arrows to move the focused resizer..
|
||||
/// </summary>
|
||||
public static string KeyboardControlsDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("KeyboardControlsDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Keyboard Navigation:.
|
||||
/// </summary>
|
||||
public static string KeyboardControlsName {
|
||||
get {
|
||||
return ResourceManager.GetString("KeyboardControlsName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Create layouts that have overlapping zones.
|
||||
/// </summary>
|
||||
|
||||
@@ -336,6 +336,17 @@
|
||||
<value>Merge/Delete:</value>
|
||||
<comment>Title for concept behind Merging two zones together or removing an zone</comment>
|
||||
</data>
|
||||
<data name="KeyboardControlsName" xml:space="preserve">
|
||||
<value>Keyboard Navigation:</value>
|
||||
</data>
|
||||
<data name="KeyboardControlsDescription" xml:space="preserve">
|
||||
<value>
|
||||
- [Shift]+S to split currently focused zone.
|
||||
- Ctrl+Tab to focus zones/resizers.
|
||||
- Tab to cycle zones and resizers.
|
||||
- Delete to remove the focused resizer.
|
||||
- Arrows to move the focused resizer.</value>
|
||||
</data>
|
||||
<data name="SplitterDescription" xml:space="preserve">
|
||||
<value>Hold Shift key for vertical split.</value>
|
||||
<comment>A segmenter visual for splitting one item into two. This would be the vertical line. Shift key is referring to key on keyboard</comment>
|
||||
|
||||
@@ -33,6 +33,9 @@ namespace FancyZonesEditor.Utils
|
||||
// Non-localizable string: Multi-monitor id
|
||||
private const string MultiMonitorId = "FancyZones#MultiMonitorDevice";
|
||||
|
||||
// Non-localizable string: default virtual desktop id
|
||||
private const string DefaultVirtualDesktopGuid = "{00000000-0000-0000-0000-000000000000}";
|
||||
|
||||
private readonly IFileSystem _fileSystem = new FileSystem();
|
||||
|
||||
private readonly JsonSerializerOptions _options = new JsonSerializerOptions
|
||||
@@ -725,7 +728,13 @@ namespace FancyZonesEditor.Utils
|
||||
bool unused = true;
|
||||
foreach (Monitor monitor in monitors)
|
||||
{
|
||||
if (monitor.Device.Id == device.DeviceId)
|
||||
string deviceIdSaved = monitor.Device.Id.Substring(0, monitor.Device.Id.LastIndexOf("_"));
|
||||
string deviceIdReadFromSettings = device.DeviceId.Substring(0, device.DeviceId.LastIndexOf("_"));
|
||||
|
||||
string virtualDesktopIdSaved = monitor.Device.Id.Substring(monitor.Device.Id.LastIndexOf("_") + 1);
|
||||
string virtualDesktopIdReadFromSettings = device.DeviceId.Substring(device.DeviceId.LastIndexOf("_") + 1);
|
||||
|
||||
if (deviceIdSaved == deviceIdReadFromSettings && (virtualDesktopIdSaved == virtualDesktopIdReadFromSettings || virtualDesktopIdReadFromSettings == DefaultVirtualDesktopGuid))
|
||||
{
|
||||
var settings = new LayoutSettings
|
||||
{
|
||||
|
||||
@@ -409,6 +409,24 @@
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";KeyboardControlsDescription" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[]D;]A; - [Shift]5D;+S to split currently focused zone.]D;]A; - Ctrl+Tab to focus zones/resizers.]D;]A; - Tab to cycle zones and resizers.]D;]A; - Delete to remove the focused resizer.]D;]A; - Arrows to move the focused resizer.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[]D;]A; – [Shift]5D;+ S: rozdělení aktuálně zaměřenou zóny]D;]A; – Ctrl+Tab: zaměření na zónu/změny velikosti]D;]A; – Tabulátor: cyklování zón a změny velikosti]D;]A; – Delete: odstranění zaměřené změny velikosti]D;]A; – Šipky: posunutí prioritní změny velikosti]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";KeyboardControlsName" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Keyboard Navigation:]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Navigace klávesnicí:]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";Layout_Canvas_Description" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Create layouts that have overlapping zones]]></Val>
|
||||
|
||||
@@ -409,6 +409,24 @@
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";KeyboardControlsDescription" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[]D;]A; - [Shift]5D;+S to split currently focused zone.]D;]A; - Ctrl+Tab to focus zones/resizers.]D;]A; - Tab to cycle zones and resizers.]D;]A; - Delete to remove the focused resizer.]D;]A; - Arrows to move the focused resizer.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[]D;]A; – [UMSCHALT]5D;+S zum Teilen der Zone, auf der der Fokus liegt.]D;]A; – STRG+TAB zum Platzieren des Fokus auf Zonen/Größenänderungen.]D;]A; – TAB zum Durchlaufen von Zonen und Größenänderungen.]D;]A; – ENTF zum Entfernen der Größenänderung, auf der der Fokus liegt.]D;]A; – Pfeiltasten zum Verschieben der Größenänderung, auf der der Fokus liegt.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";KeyboardControlsName" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Keyboard Navigation:]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Navigation über die Tastatur:]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";Layout_Canvas_Description" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Create layouts that have overlapping zones]]></Val>
|
||||
|
||||
@@ -409,6 +409,24 @@
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";KeyboardControlsDescription" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[]D;]A; - [Shift]5D;+S to split currently focused zone.]D;]A; - Ctrl+Tab to focus zones/resizers.]D;]A; - Tab to cycle zones and resizers.]D;]A; - Delete to remove the focused resizer.]D;]A; - Arrows to move the focused resizer.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[]D;]A; - [Shift]5D;+ S para dividir la zona que tiene el foco actualmente.]D;]A; - Ctrl+Tab para enfocar zonas y cambiar tamaño.]D;]A; - Tab para desplazamiento de zonas y cambiar tamaño.]D;]A; - Suprimir para quitar el redimensionador que tiene el foco.]D;]A; - Flechas para mover el redimensionador que tiene el foco.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";KeyboardControlsName" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Keyboard Navigation:]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Navegación con el teclado:]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";Layout_Canvas_Description" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Create layouts that have overlapping zones]]></Val>
|
||||
|
||||
@@ -409,6 +409,24 @@
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";KeyboardControlsDescription" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[]D;]A; - [Shift]5D;+S to split currently focused zone.]D;]A; - Ctrl+Tab to focus zones/resizers.]D;]A; - Tab to cycle zones and resizers.]D;]A; - Delete to remove the focused resizer.]D;]A; - Arrows to move the focused resizer.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[]D;]A; - [Maj]5D;+S pour fractionner la zone actuellement ciblée.]D;]A; -Ctrl+Tabulation pour cibler les zones/les redimensionneurs.]D;]A; -Onglet pour faire défiler les zones et les redimensionneurs.]D;]A; -Supprimer pour supprimer le redimensionneur ciblé.]D;]A; -Flèches pour déplacer le redimensionneur ciblé.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";KeyboardControlsName" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Keyboard Navigation:]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Navigation au clavier :]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";Layout_Canvas_Description" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Create layouts that have overlapping zones]]></Val>
|
||||
|
||||
@@ -409,6 +409,24 @@
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";KeyboardControlsDescription" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[]D;]A; - [Shift]5D;+S to split currently focused zone.]D;]A; - Ctrl+Tab to focus zones/resizers.]D;]A; - Tab to cycle zones and resizers.]D;]A; - Delete to remove the focused resizer.]D;]A; - Arrows to move the focused resizer.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[]D;]A; – A [Shift]5D;+S billentyűkkel feloszthatja a jelenleg fókuszban lévő zónát.]D;]A; – A Ctrl+Tab billentyűkkel ráfókuszálhat zónákra/méretezőkre.]D;]A; – A Tab billentyűvel váltogathat a zónék és méretezők között.]D;]A; – A Delete billentyűvel eltávolíthatja a fókuszban lévő méretezőt.]D;]A; – A nyílbillentyűkkel mozgathatja a fókuszban lévő méretezőt.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";KeyboardControlsName" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Keyboard Navigation:]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Billentyűzet alapú navigáció:]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";Layout_Canvas_Description" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Create layouts that have overlapping zones]]></Val>
|
||||
|
||||
@@ -409,6 +409,24 @@
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";KeyboardControlsDescription" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[]D;]A; - [Shift]5D;+S to split currently focused zone.]D;]A; - Ctrl+Tab to focus zones/resizers.]D;]A; - Tab to cycle zones and resizers.]D;]A; - Delete to remove the focused resizer.]D;]A; - Arrows to move the focused resizer.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[]D;]A; - [MAIUSC]5D;+S per dividere la zona attiva corrente.]D;]A; - Ctrl+TAB per attivare zone/ridimensionamenti.]D;]A; - TAB per ripetere zone e ridimensionamenti.]D;]A; - Elimina per rimuovere il ridimensionamento attivo.]D;]A; - Frecce per spostare il ridimensionamento attivo.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";KeyboardControlsName" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Keyboard Navigation:]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Navigazione tastiera:]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";Layout_Canvas_Description" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Create layouts that have overlapping zones]]></Val>
|
||||
|
||||
@@ -409,6 +409,24 @@
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";KeyboardControlsDescription" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[]D;]A; - [Shift]5D;+S to split currently focused zone.]D;]A; - Ctrl+Tab to focus zones/resizers.]D;]A; - Tab to cycle zones and resizers.]D;]A; - Delete to remove the focused resizer.]D;]A; - Arrows to move the focused resizer.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[]D;]A; - Shift+S で現在フォーカスが置かれているゾーンを分割します。]D;]A; - Ctrl+Tab でゾーンと Resizer でフォーカスを切り替えます。]D;]A; - Tab でゾーンと Resizer を順番に移動します。]D;]A; - Delete でフォーカスが置かれている Resizer を削除します。]D;]A; - 矢印キーで、フォーカスの置かれた Resizer を移動します。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";KeyboardControlsName" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Keyboard Navigation:]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[キーボード ナビゲーション:]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";Layout_Canvas_Description" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Create layouts that have overlapping zones]]></Val>
|
||||
|
||||
@@ -409,6 +409,24 @@
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";KeyboardControlsDescription" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[]D;]A; - [Shift]5D;+S to split currently focused zone.]D;]A; - Ctrl+Tab to focus zones/resizers.]D;]A; - Tab to cycle zones and resizers.]D;]A; - Delete to remove the focused resizer.]D;]A; - Arrows to move the focused resizer.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[]D;]A; -<Shift+S>를 눌러 현재 포커스가 된 영역을 분할합니다.]D;]A; -<Ctrl+Tab>을 눌러 영역/리사이저의 초점을 맞춥니다.]D;]A; -<Tab> 키를 눌러 영역 및 리사이저를 회전합니다.]D;]A; -<Delete>를 눌러 포커스가 된 리사이저를 제거합니다.]D;]A; -<Arrows> 키를 눌러 포커스가 된 리사이저를 이동합니다.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";KeyboardControlsName" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Keyboard Navigation:]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[키보드 탐색:]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";Layout_Canvas_Description" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Create layouts that have overlapping zones]]></Val>
|
||||
|
||||
@@ -409,6 +409,24 @@
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";KeyboardControlsDescription" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[]D;]A; - [Shift]5D;+S to split currently focused zone.]D;]A; - Ctrl+Tab to focus zones/resizers.]D;]A; - Tab to cycle zones and resizers.]D;]A; - Delete to remove the focused resizer.]D;]A; - Arrows to move the focused resizer.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[]D;]A; - [Shift]5D;+S om de zone in focus te splitsen.]D;]A; - Ctrl+Tab om zones/formaataanpassingen te selecteren.]D;]A; - Tab voor een eenvoudige cyclus van zones en formaataanpassingen.]D;]A; - Delete om de formaataanpassing in focus te verwijderen.]D;]A; - Pijlen om de formaataanpassing in focus te verplaatsen.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";KeyboardControlsName" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Keyboard Navigation:]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Toetsenbordnavigatie:]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";Layout_Canvas_Description" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Create layouts that have overlapping zones]]></Val>
|
||||
|
||||
@@ -409,6 +409,24 @@
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";KeyboardControlsDescription" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[]D;]A; - [Shift]5D;+S to split currently focused zone.]D;]A; - Ctrl+Tab to focus zones/resizers.]D;]A; - Tab to cycle zones and resizers.]D;]A; - Delete to remove the focused resizer.]D;]A; - Arrows to move the focused resizer.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[]D;]A; — [Shift]5D;+ S, aby podzielić strefę priorytetową.]D;]A; — Ctrl+Tab do koncentracji stref/wymiarów.]D;]A; —Tab do tworzenia stref i wymiarów.]D;]A; — DELETE, aby usunąć wskaźnik priorytetowy.]D;]A; — Strzałki, aby przenieść wskaźnik priorytetowy.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";KeyboardControlsName" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Keyboard Navigation:]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Nawigacja za pomocą klawiatury:]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";Layout_Canvas_Description" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Create layouts that have overlapping zones]]></Val>
|
||||
|
||||
@@ -409,6 +409,24 @@
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";KeyboardControlsDescription" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[]D;]A; - [Shift]5D;+S to split currently focused zone.]D;]A; - Ctrl+Tab to focus zones/resizers.]D;]A; - Tab to cycle zones and resizers.]D;]A; - Delete to remove the focused resizer.]D;]A; - Arrows to move the focused resizer.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[]D;]A; - [Shift]5D;+S para dividir a zona atualmente focada.]D;]A; - Ctrl+Tab para focar zonas/redimensionadores.]D;]A; - Tab para ciclos de zonas e redimensionadores.]D;]A; - Delete para remover o redimensionador em foco.]D;]A; - Setas para mover o redimensionador em foco.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";KeyboardControlsName" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Keyboard Navigation:]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Navegação pelo Teclado:]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";Layout_Canvas_Description" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Create layouts that have overlapping zones]]></Val>
|
||||
|
||||
@@ -409,6 +409,24 @@
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";KeyboardControlsDescription" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[]D;]A; - [Shift]5D;+S to split currently focused zone.]D;]A; - Ctrl+Tab to focus zones/resizers.]D;]A; - Tab to cycle zones and resizers.]D;]A; - Delete to remove the focused resizer.]D;]A; - Arrows to move the focused resizer.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[]D;]A; - [Shift]5D;+S para dividir zona atualmente focada.]D;]A; - Ctrl+Tab para focar zonas/redimensionamentos.]D;]A; - Tab para circular zonas e redimensionamentos.]D;]A; - Delete para remover o redimensionamento focado.]D;]A; - Setas para mover o redimensionamento focado.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";KeyboardControlsName" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Keyboard Navigation:]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Navegação com teclado:]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";Layout_Canvas_Description" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Create layouts that have overlapping zones]]></Val>
|
||||
|
||||
@@ -409,6 +409,24 @@
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";KeyboardControlsDescription" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[]D;]A; - [Shift]5D;+S to split currently focused zone.]D;]A; - Ctrl+Tab to focus zones/resizers.]D;]A; - Tab to cycle zones and resizers.]D;]A; - Delete to remove the focused resizer.]D;]A; - Arrows to move the focused resizer.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[]D;]A; – [SHIFT]5D;+S, чтобы разделить зону текущего фокуса.]D;]A; – CTRL+TAB, чтобы сфокусироваться на зонах/изменении размеров.]D;]A; – TAB для циклического перехода между зонами и изменением размеров.]D;]A; – DELETE, чтобы удалить изменение размера в фокусе.]D;]A; – Кнопки со стрелками для перемещения изменения размера в фокусе.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";KeyboardControlsName" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Keyboard Navigation:]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Навигация с помощью клавиатуры:]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";Layout_Canvas_Description" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Create layouts that have overlapping zones]]></Val>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user