Compare commits

...

69 Commits

Author SHA1 Message Date
Niels Laute
bc7d1bc7b2 [ColorPicker] Fix contrast issue (#13450)
* Update ColorFormatControl.xaml

* Update ColorFormatControl.xaml
2021-09-27 16:03:14 +01:00
Jaime Bernardo
26e8d0d976 [OOBE] Fix starting PowerToys Run (#13455) 2021-09-27 15:55:00 +01:00
Seraphima Zykova
acadd069e8 [FancyZones Editor] Fix the editor crash (#13463) 2021-09-27 15:35:33 +01:00
csigs
6e4a5a4bb6 LEGO: check in for master to temporary branch. (#13461) 2021-09-27 15:20:23 +01:00
Jaime Bernardo
6212c1bb80 [Color Picker] Increase width of editor window (#13420) 2021-09-24 16:54:58 +01:00
Ruben Fricke
3358fd9b02 [Color Picker] Feature/lab color representation (#12935)
* Created logic to convrt RGB to CIELAB (with intermediate step to CIEXYZ)

* Added CIELAB to the available color representation types

* Created tests for the color conversion from RGB to LAB (and for RGB to XYZ)

* Update ColorPickerViewModel to keep the L*a*b* format the same

* Improved variable names & comment

* Remove url from color converting website to avoid unnecessary license issues

* Removed typo of the wrong variable

* Added expected words into dictionary

* Added links to explain used formulas

* Added CIE XYZ color space

* Added 'SRGB' to the dictionary

* Updated the range for the X and Z value in the CIE XYZ color space comments

* Fixed XYZ to LAB calculations

* Changed output format for CIELAb

Changed L*a*b*(L,a,b) to CIELab(L,a,b)

* Changed output in tests

* Fixed tests

* Added extra accuracy

* Add decimal places to cielab and ciexyz formats

Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
2021-09-24 16:54:44 +01:00
Andrey Nekrasov
88e24263cf [Setup] Don't force default installer location when it wasn't specified (#13397) 2021-09-23 18:09:52 +01:00
Jaime Bernardo
d7098e87ee [PTRun] Fix crash when shutting Windows down (#13401) 2021-09-23 18:09:38 +01:00
Seraphima Zykova
64ecb553e4 [CI] fix build for include sstream (#13400) 2021-09-23 18:09:24 +01:00
Seraphima Zykova
2130cef3a9 [FancyZones Editor] Fix crash on closing after deleting a custom layout. (#13394) 2021-09-23 16:24:24 +01:00
Jaime Bernardo
f647223e94 [Shortcut Guide] Activate with Windows key press (#13342)
* [Shortcut Guide] Activate with Windows key press

* fix spellchecker

* pr comments: fix search and add lock

* Add activation method combo box

* fix spellchecker issue for customized

* Standardize centralized hotkeys file names

* Add warning when using the long win key method

* Address PR feedback on text

* More PR feedback
2021-09-23 14:23:22 +01:00
Heiko
a0ebe5ed54 [Code refactoring] Make code comment and NullOrEmpty check more clear (#13352)
* Clarify code

* grammar fix

* comment change
2021-09-22 22:43:28 +01:00
Seraphima Zykova
b05378cdf7 [FancyZones] Reset zone settings after restart fix. (#13322)
* updated virtual desktop retrieving

* save with null-guid

* moved guid utils

* moved DeviceIdData related functions

* replaced strings with data structs

* default value

* clean up

* save app zone history with zones settings

* compare with null guid

* updated tests

* refactoring

* logs

* sync vd ids

* logs

* refactoring

* check virtual desktop id

* OnDisplayChange call

* compare device ids in editor
2021-09-22 22:39:48 +01:00
Seraphima Zykova
423faf7af1 [FZ Editor] "Save" and "Cancel" buttons are hidden in the grid layout editor (#13337)
Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
2021-09-22 14:38:46 +01:00
Heiko
f9bb7ba270 [ImageResizer] Default values for new size (#13285)
* Code changes

* small fix in unittests

* Add new Unit Test

* Fix tests

* fix typo

* remove unused code from unit test

* Update resource string name

* Add name counter

* comment update

* Update tests
2021-09-21 23:55:42 +01:00
Clint Rutkas
4bc2de7b7b Store readme update (#13267)
* Update README.md

* Update README.md

* Update README.md

* Update akaLinks.md

* Update excludes.txt

ignoring aka link page

* Update README.md

* Update README.md

* Update README.md

Co-authored-by: Heiko <61519853+htcfreek@users.noreply.github.com>

Co-authored-by: Heiko <61519853+htcfreek@users.noreply.github.com>
2021-09-21 10:07:01 -07:00
Niels Laute
d199b41937 [Image Resizer] ComboBox UI (#13321)
* UI updates

* Added combobox

* Updated UI

* Updated UI

Co-authored-by: Niels Laute <niels9001@hotmail.com>
2021-09-21 18:44:48 +02:00
Andrey Nekrasov
7fe63cdb50 [Setup] add support for embedded MSIX packages (#13263) 2021-09-21 15:15:59 +03:00
csigs
f4531130b6 LEGO: check in for master to temporary branch. (#13317) 2021-09-20 21:50:10 +01:00
Davide Giacometti
c6a24b3907 [PT Run][Registry] handle invalid base keys (#13284) 2021-09-20 11:35:39 +01:00
Niels Laute
2c44cc9cd2 [Settings] UI bugfixes (#13260)
* Fixed HyperlinkButton style

* Bugfixes to NavView

* Update ShellPage.xaml

* Replaced Windows 10 with Windows

* Updated strings

* Tweaks

* Reducing gif file sizes

* Add InfoBadge

* Updated ImageResizer UI

* Updated strings

* Updated string

* Update KBM.gif

* Updated icons

* Update KeyVisual.xaml

Co-authored-by: Niels Laute <niels9001@hotmail.com>
2021-09-19 16:22:39 +02:00
Jaime Bernardo
739737dc2b Adding in Heiko to community.md (#13251)
* Adding in Heiko to community.md

* fix spellchecker
2021-09-17 15:50:21 +01:00
Jaime Bernardo
6bd0840863 [PT Run][Registry] show no results on query fail (#13244) 2021-09-17 15:49:40 +01:00
Jaime Bernardo
323f41d14b [ci] Fix pdf thumbnail unit tests flakiness (#13248)
* [ci] reduce pdf preview and thumbnail flakiness

* [ci] run pdf thumbnail tests

* [meta] add pdf modules to new issue template
2021-09-16 18:02:50 +01:00
Jaime Bernardo
8115a709e9 [Settings] Upgrade WinUI to 2.7.0 (#13226) 2021-09-16 17:16:01 +01:00
Heiko
b0344f1426 [PT Run][Installer] Add loc files for settings plugin (#13241) 2021-09-16 15:31:30 +01:00
Heiko
9fb663210d [PT Run] EnvironmentHelper: Adding logging and error handling (#13205)
* Add error handling and logging

* improvements

* fix typos

* comment updates

* fix typos
2021-09-16 11:17:33 +01:00
Dustin L. Howett
881b1da6a7 Move away from the legacy windevbuildagents pool (#13218)
There is an internal requirement that we move to build agents that we
don't run ourselves. This discharges us of that requirement!

We're switching between the WinDevPool pool and the WinDevPoolOSS pool
based on whether this code is being built in the open-source tenant or
the internal/private one.
2021-09-15 18:30:01 -05:00
Heiko
8e350ca4a7 [PT Run] Settings plugin: Settings path filter and bug fixes (#13151)
* Create filter method

* adding call of new method

* solve bug #13150

* fixes and comment updates

* fix typos

* Update filter method

* Improve performance on empty queries

* fix typos
2021-09-15 18:37:36 +01:00
Deondre Davis
a0a355c3a7 Update README.md for v0.46 release (#13210)
* Update README.md

* Update README.md
2021-09-14 10:00:28 -07:00
R. de Veen
7ac1e00d01 [FileExplorer] Add PDF Thumbnail Provider for Windows Explorer (#13058)
* Add PdfPreviewHandler to build dependency of runner

* PDF Thumbnail Provider

* Remove using brackets

* Pdf Thumbnail - Settings and Unit Tests

* Removed resx

* Add PDF Thumbnail Provider binary

* Install Pdf Thumbnail Provider

* Fix pagee spelling error.

* Update Windows dependency to version 10.0.18362.0 because that is the minimal required version of the PowerToys.

* Add Pdf Preview Handler and Pdf Thumbnail Provider
2021-09-14 17:01:45 +01:00
csigs
7e22f26b52 LEGO: check in for master to temporary branch. (#13193) 2021-09-14 11:17:16 +01:00
csigs
4f02795c60 LEGO: check in for master to temporary branch. (#13192) 2021-09-14 11:17:07 +01:00
csigs
f430aff4af LEGO: check in for master to temporary branch. (#13191) 2021-09-14 11:16:56 +01:00
csigs
f05d64b01e LEGO: check in for master to temporary branch. (#13190) 2021-09-14 11:16:47 +01:00
csigs
cae6bdf437 LEGO: check in for master to temporary branch. (#13189) 2021-09-14 11:16:34 +01:00
csigs
7d8f7aafdd LEGO: check in for master to temporary branch. (#13188) 2021-09-14 11:16:17 +01:00
Jaime Bernardo
5d279ed22c [ci] restore bootstrapper nuget packages (#13201) 2021-09-14 11:11:22 +01:00
csigs
1edd52014e LEGO: check in for master to temporary branch. (#13183) 2021-09-13 14:22:43 +01:00
Davide Giacometti
1f2f247c1d Consolidate nuget packages (#13122)
* consolidate nuget packages

* updated wil nuget
2021-09-13 13:00:44 +01:00
Andrey Nekrasov
7c2a807278 [VCM] add comment how to enable the corresponding OOBE page (#13145) 2021-09-10 14:15:16 +03:00
Franky Chen
21c7374177 Fix typo #13117 (#13128)
* Fix typo #13117

* Fix typo in comments
2021-09-09 14:34:19 +01:00
lcjh
1495285a6a fix typo (#13116) 2021-09-09 14:32:54 +01:00
Andrey Nekrasov
bdeb96d0a7 [CI] Sign VCM x86 module dll (#13129) 2021-09-09 14:32:08 +01:00
Jaime Bernardo
5963294b04 [PowerToys Run] Use global HotKey instead of low level keyboard hook (#13114)
* [PowerToys Run] Register global HotKey

Using low level keyboard hooks caused focus issues when invoking
PowerToys Run. Using a global HotKey solves this issue.

* Properly unregister hotkey on dispose

* fix spellchecker errors
2021-09-08 18:39:51 +01:00
csigs
2c58bdbfb2 LEGO: check in for master to temporary branch. (#13073) 2021-09-08 17:27:48 +01:00
Davide Giacometti
f00bf7cf19 [Settings] update additional options for launcher plugins (#12920) 2021-09-06 23:19:00 +01:00
Davide Giacometti
2fe6282157 [PT Run] Option for system commands localization (#12939) 2021-09-06 23:18:14 +01:00
Niels Laute
a6cca7cfb0 [Settings] Shortcut UX redesign (#12977)
* Added dialog

* Added virtualkey logic

* Key logic in place

* Styling

* Updated layout

* Updated foreground

* Refactor

* Catching error handling

* Hotkey UI handling

* Spell check

* Adding shortcut visuals to OOBE

* INtroducing shortcutcontrol for OOBE

* Removed unneccasry comments

* OOBE fixes

* Fix

* Visual updates

* Update Product.wxs

* Updated UI

* Update Product.wxs

* Changes

* Changed installer file

* Fixed warner banner height

* Added visual key to KBR

* Updated margin

* pr 12977: fix installer issue (#13075)

Co-authored-by: Niels Laute <niels9001@hotmail.com>
Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
2021-09-06 20:21:18 +02:00
Heiko
2bfc62d9a5 [PT Run] [WindowsSettings plugin] Translation improvements and LocProject.json (#13078)
* TranslationHelper: Update/add if conditions

* Resource strings: Update <ControlPanel>

* Creating <LocProject.json>
2021-09-06 18:17:20 +01:00
Heiko
dcc4563c8c [WindowsSettings Plugin] Improve subtitle and area naming (#12915)
* Update WindowsSettings clas

* Update TranslationHelper

* Adding ToDos to code

* Adding ToDo notes

* Introduce AreaPathHelper

* Small improvement

* Improve helper class

* Update ResultHelper

* Fix spelling

* adding todo marker

* Improve calling of <WindowsSettingsPathHelper>: Update class <WindowsSetting>

* Improve calling of <WindowsSettingsPathHelper>: Update helper class

* Improve calling of <WindowsSettingsPathHelper>: Implement method call

* TranslationHelper: Small fixes

* Fix scoring for areas property

* Fix search filters for area property

* small fixes

* Update WindowsSettings.json

* adding todos

* Code cleanup, improvements and fixes

* Small changes

* Update resource strings

* Fix msitakes

* Update settings

* resolve review feedback
2021-09-06 11:46:14 +01:00
Mykhailo Pylyp
a4f84844bc [Keyboard Manager] Alt+Tab navigation with arrow keys i (#12986)
* Draft

# Conflicts:
#	src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.cpp

* Handle key case

* Fix key count

* Remember if win key was pressed

* Don't search twice, remove redundant assignment

* spelling
2021-09-04 08:49:13 +03:00
Niels Laute
941221cba9 Added images (#13032)
Co-authored-by: Niels Laute <niels9001@hotmail.com>
2021-09-03 15:14:55 -07:00
Davide Giacometti
0b6d654ec8 Thumbnail handlers reload (#12996) 2021-09-03 21:24:23 +01:00
Jaime Bernardo
b8236d55e2 [KBM Editor] fix crash when mapping left and right modifier to the combined key. (#12999)
* [KBM Editor] Don't combine keys to same key

* Avoid crashes when flyouts can't be shown yet

* Disallow mapping of left or right key to combined

* Refactor remap to combined key check

* Add log message when flyout fails to load
2021-09-03 16:19:16 +01:00
Jaime Bernardo
4a1e21ac83 [KBM Editor] Remove UpdateLayout calls from shortcut UI (#12857)
* [KBM Editor] Remove updatelayout from shortcut UI

* Restore some UpdateLayout
2021-09-03 14:19:38 +01:00
csigs
ee69dc5e44 LEGO: check in for master to temporary branch. (#12952) 2021-09-03 10:56:44 +01:00
Deondre Davis
fd06c52dec v0.45 Updates (#12987)
* Update README.md

* Updated Readme and expect.text

* Update expect.txt

* Update README.md

* Update README.md

* Update README.md

Co-authored-by: Aaron Junker <vl5ktlnki@relay.firefox.com>

* Update README.md

* Update README.md

Co-authored-by: Clint Rutkas <clint@rutkas.com>
Co-authored-by: Aaron Junker <vl5ktlnki@relay.firefox.com>
2021-09-02 11:52:06 -07:00
Tamás Hegedűs
91910b4606 [Common] Removed outdated comment (#12994)
The retry loop has been removed as of https://github.com/microsoft/PowerToys/pull/12860 , but the comment stayed.
2021-09-02 15:00:43 +01:00
Andrey Nekrasov
f10faf004e [FancyZonesEditor]: Grid Editor keyboard control (#12969)
- Ctrl+Tab to switch between zones and layout overlay window
- Tab to focus between grid zones and resizers
- While resizer is focused: arrows to move it; Del to remove it
- While zone is focused: (Shift)+S to split it horizontally/vertically
2021-09-01 21:23:10 +03:00
Mykhailo Pylyp
f0750997de [Bug Report Tool] Zip folder can not be created (#12966) 2021-09-01 18:12:28 +03:00
Seraphima Zykova
f7333c89bb [FancyZones][Settings] "Allow zones to span across monitors" prerequisites description. (#12913) 2021-08-31 16:23:00 +01:00
Mykhailo Pylyp
19fb3fd56c Remove subset case as it is automaticly handled by more generic one (#12902) 2021-08-31 12:43:04 +03:00
Davide Giacometti
d197ddeb15 handling malformed json (#12942) 2021-08-30 20:37:04 +02:00
martinchrzan
46bfd2cdac Fixing the issue with HEX textbox not being updated with the correct RGB values (#12936)
* Fixing the issue with hex textbox not being updated with the correct values from RGB boxes #12931

* Update current color immediately when typing in RGB boxes #11108

* added expected words into dictionary
2021-08-30 09:44:21 +02:00
Seraphima Zykova
45b5545cf7 [OOBE] Updated KBM preview gif (#12932) 2021-08-27 18:41:09 +01:00
Niels Laute
e881043787 Updated strings for powerrename (#12927)
Co-authored-by: Niels Laute <niels9001@hotmail.com>
2021-08-27 16:40:53 +02:00
Seraphima Zykova
2b95eba51b check desktop id (#12930) 2021-08-27 14:59:04 +01:00
R. de Veen
4177708e49 Enable PDF files in preview pane (#9088)
## Summary of the Pull Request
This PR enables user to preview PDF files in the Explorer preview pane
and in Outlook. 

**What is this about:**
Windows does not support out of the box experience for previewing PDF
files in the preview pane. Users need to install third-party software
like Adobe Acrobat reader. The PdfPreviewHandler module enbales the user
to preview PDF files.

**How does someone test / validate:** 
Run the installer, open Explorer and select a PDF file, enable the
preview pane. Maybe need to remove third-party PDF software.

## Quality Checklist

- [X] **Linked issue:** #3548
- [ ] **Communication:** I've discussed this with core contributors in the issue. 
- [X] **Tests:** Added/updated and all pass
- [X] **Installer:** Added/updated and all pass
- [X] **Localization:** All end user facing strings can be localized
- [ ] **Docs:** Added/ updated
- [x] **Binaries:** Any new files are added to WXS / YML
   - [ ] No new binaries
   - [x] YML for signing
   - [x] WXS for installer
2021-08-26 16:43:26 -05:00
375 changed files with 93176 additions and 18221 deletions

View File

@@ -39,6 +39,8 @@ body:
- Image Resizer
- Keyboard Manager
- MD Preview
- PDF Preview
- PDF Thumbnail
- PowerRename
- PowerToys Run
- Shortcut Guide

View File

@@ -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$

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,3 @@
cd /D "%~dp0"
nuget restore ../installer/PowerToysBootstrapper/PowerToysBootstrapper.sln || exit /b 1

View File

@@ -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 :)

View File

@@ -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
View File

@@ -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

View File

@@ -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 |

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 KiB

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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?>

View File

@@ -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:

View File

@@ -16,4 +16,6 @@ EXPORTS
TerminateProcessesCA
CertifyVirtualCameraDriverCA
InstallVirtualCameraDriverCA
InstallEmbeddedMSIXCA
UninstallVirtualCameraDriverCA
UninstallEmbeddedMSIXCA

View File

@@ -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>

View File

@@ -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>

View 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

View 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

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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) };
}
}

View File

@@ -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
{

View File

@@ -17,7 +17,7 @@
BorderThickness="1"
Title="Color Picker"
Height="380"
Width="400"
Width="440"
ResizeMode="NoResize"
Topmost="True"
WindowStartupLocation="CenterScreen">

View File

@@ -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"

View File

@@ -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>

View File

@@ -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

View File

@@ -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>

View File

@@ -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)})";
}
}
}

View File

@@ -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;

View File

@@ -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()
{

View File

@@ -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)
{

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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;
};

View File

@@ -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;
}
}

View File

@@ -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;
}
};
}

View File

@@ -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>

View File

@@ -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">

View 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;
}

View File

@@ -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());

View File

@@ -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>;

View File

@@ -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)
{

View File

@@ -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:

View File

@@ -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;
}
}

View File

@@ -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();
};

View File

@@ -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))

View File

@@ -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;

View File

@@ -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>

View File

@@ -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;

View File

@@ -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;

View File

@@ -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>

View File

@@ -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>

View File

@@ -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);

View File

@@ -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>

View File

@@ -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());
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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>

View File

@@ -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)

View File

@@ -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 ?

View File

@@ -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"/>

View File

@@ -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>

View File

@@ -13,6 +13,8 @@
BorderBrush="{DynamicResource SystemControlBackgroundAccentBrush}"
BorderThickness="1"
Opacity="1"
Focusable="True"
IsTabStop="True"
ui:ControlHelper.CornerRadius="4"
mc:Ignorable="d">

View File

@@ -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));
}

View File

@@ -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);

View File

@@ -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()

View File

@@ -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>

View File

@@ -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>

View File

@@ -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
{

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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